前言
github相关源码上有详细的注解,请自行对照github源码
简单解说下ProcessorComponent接口上module节点的作用:
-
InjectBindingRegistryModule:对ComponentProcessor类中injectBindingRegistry变量最终实例化成InjectBindingRegistryImpl对象;
-
SourceFileGeneratorsModule:为后面的代码生成提供实例化对象;
-
ProcessingStepsModule:本章核心内容,对不同注解的逻辑处理;
-
ProcessingEnvironmentModule:为提供XProcessingEnv可提供的参数:
- 例如DaggerElements的实例化需要
XConverters.toJavac(xProcessingEnv).getElementUtils()和XConverters.toJavac(xProcessingEnv).getTypeUtils()作为参数;
-
ComponentGeneratorModule:对component节点代码生成做单独处理;
-
BindingMethodValidatorsModule:收集bindingMethod绑定方法校验;
-
BindingGraphValidationModule: 绑定图形的校验;
以上主要目的都是为实例化提供需要的参数;
直接看ProcessingStepsModule接口中的processingSteps方法,该方法的参数表示针对不同注解的处理:规则校验,生成对象,校验对象,最终生成代码等操作。
注解规则
component节点规则
该节点校验在ComponentProcessingStep类的isComponentValid方法中,如果节点使用了Component、ProductionComponent、Subcomponent、ProductionSubcomponent注解修饰使用此规则校验。
在ComponentValidator类中validate方法作为校验入口,校验规则如下:
-
component节点上的componentAnnotation注解有且仅有一个;
-
如果component节点上使用了@CancellationPolicy注解修饰,那么当前component节点只能使用@ProductionSubcomponent或@ProductionComponent注解;
-
component节点只能是abstract抽象类或接口;
-
component节点最多只能存在一个creator节点,自行查看下面的creator的校验;
-
component节点不能使用@Reusable修饰;
-
如果component是一个kotlin文件,那么componentMethod方法名不能使用java关键字,自行查看下面componentMethod方法校验;
-
component节点中的入口(方法是abstract修饰或接口方法 && 方法无参 && 方法返回类型不是void && 方法没有使用泛型)并且该方法没有被重写,那么这个方法最多只能有一个;
-
本条根据代码翻译过来的,因为这个校验使用了EntryPoints,所以叫入口方法,但是个人理解下(1)这个绝对不是入口方法(ProcessorComponent接口中
void inject(ComponentProcessor processor)是货真价实的入口方法);(2)入口方法根据后面意思的理解也不是只存在一个; -
顺便理解下入口方法,入口方法是非private、非static修饰的abstract方法或接口方法,并且该方法的参数表示需要通过当前component节点进行实例化注入;
-
componentMethod方法返回类型是subcomponent或subcomponent.creator,该方法最多只能出现一个;
-
componentAnnotation#dependencies里面的类不能是module节点;
-
componentAnnotation#modules校验,自行查看;
-
还需要对当前componentMethod方法返回类型是subcomponent(或subcomponent.creator)的subcomponent节点进行校验,从步骤1开始。
componentMethod节点校验规则
这里仅仅针对abstract修饰(或接口方法)、非private、非static的(包括从父级类继承过来的)componentMethod方法做校验,规则如下:
-
componentMethod方法不能使用泛型,如果当前component节点是kotlin文件,那么注意componentMethod不能使用java关键字;
-
如果componentMethod方法的returnType返回类型是subcomponent节点,校验:
-
(1)当前componentMethod方法的参数必须是module节点,并且该方法只允许一次同一类型的module节点,并且这个module节点来源于(2)-subcomponent关联的module节点;
-
(2)收集subcomponent关联的module节点:①subcomponentAnnotation#modules里面的module节点;②条件①module节点上的注解moduleAnnotation#includes里面的module节点;③条件①和条件②的module节点的父级module(使用了moduleAnnotation注解)节点;
-
componentMethod方法返回类型是subcomponent.creator节点,当前方法参数必须为空,并且对creator节点校验,自行查看;
-
方法返回类型不是subcomponent节点也不是subcomponent.creator节点,那么参数最多只允许有一个,并且对无参和有一个参数的情况分别校验:
-
(1)componentMethod方法无参,对当前componentMethod方法和方法返回类型returnType做依赖校验:
-
注:returnType判断是否RequestKind类型(自行查看RequestKind,表示是否被RequestKind中相关架构包裹),如果是,剥离RequestKind外壳,获取T作为keyType
-
① 如果componentMethod方法使用了Assisted注解,那么不继续下面的校验(条件不可能成立,因为Assisted仅仅支持修饰参数);;
-
② componentMethod方法上最多只允许被一个Qualifier修饰的注解修饰;
-
③ 如果componentMethod没有被Qualifier修饰的注解修饰,那么keyType节点的构造函数不允许使用@AssistedInject修饰;
-
④ 如果componentMethod没有被Qualifier修饰的注解修饰,并且keyType节点使用了@AssistedFactory修饰,那么returnType不能是RequestKind中的Lazy、Producer或Produced,要么是T,要么是Provider;
-
⑤ keyType不允许使用通配符;
-
⑥ 如果keyType是MembersInjector类型,对T校验(必须存在T):
-
a. T节点不允许使用Qualifier修饰的注解修饰;
-
b. T只能是类或接口,如果是泛型,那么泛型里面只允许是类或接口或数组,数组只允许是类或接口或数组,类或接口不支持例如List类型(必须使用List样式);
-
(2) componentMethod方法返回类型不是subcomponent节点也不是subcomponent.creator节点,并且有且仅有一个参数,该方法和方法参数做成员注入校验:
-
① 对componentMethod方法和该方法参数进行校验,该componentMethod方法和方法参数都不能使用Qualifier修饰的注解修饰;并且方法参数类型只能是类或接口,如果使用泛型,那么泛型只能使用类或接口或数组,数组必须是类或数组,泛型不允许使用例如List(必须使用List)样式;
-
② 该componentMethod方法返回类型要么是void,要么参数类型和方法返回类型一致;
creator节点校验规则
使用Component.Builder、Component.Factory、Subcomponent.Builder、Subcomponent.Factory、ProductionComponent.Builder、ProductionComponent.Factory、ProductionSubcomponent.Builder、ProductionSubcomponent.Factory注解的节点。
在ComponentCreatorValidator类的validate方法作为校验入口,该节点规则如下:
-
creator节点上的creatorAnnotation注解最多只能使用一个;
-
creator节点所在的父级节点一定是component节点;
-
creator节点只能是类或接口,并且如果是类的话,该类只能存在一个无参非private修饰的构造函数;
-
creator节点不能使用泛型,并且creatorMethod不能使用private修饰,必须使用static和abstract修饰(或接口方法):
- 存在疑点,creator如果是一个接口,creatorMethod没有使用static和abstract修饰也是没有问题的;
- 如果creator节点是factory类型:
-
(1)非private、非static、abstract修饰的(包括继承的)的factoryMethod方法有且仅有一个;
-
(2)factoryMethod方法不允许使用泛型;
-
(3)factoryMethod方法返回类型必须是component节点(factoryMethod父级creator节点的父级component节点)或component的继承类;
-
注:factoryMethod方法返回类型是component节点的继承类,并且component节点中的如果没有componentMethod方法,会报警告;
-
(4)factoryMethod方法不能使用@BindsInstance修饰;
-
(5) factoryMethod方法参数要么是@BindsInstance修饰 || 要么不使用原始类型;
- 如果creator节点是builder类型:
-
(1)如果当前builder是kotlin文件,那么builderMethod不要使用了java的关键字;
-
(2)builderMethod方法总共能有两种:有且仅有一个参数的setterMethod方法和无参的buildMethod方法:
-
① buildMethod方法有且仅有一个,并且不允许使用泛型类型;
-
② buildMethod方法返回类型必须是componente节点(buildMethod方法父级creator节点的父级component节点)或component继承类;
-
注:buildMethod方法返回类型是component节点的继承类,并且component节点中的如果没有componentMethod方法,会报警告;
-
③ buildMethod方法不允许使用@BindsInstance修饰;
-
④ setterMethod方法不能使用泛型,并且方法返回类型是void || 是builder节点及其子类;
-
⑤ setterMethod方法和方法参数不允许同时使用@BindsInstance修饰,如果setterMethod方法和方法参数都没有使用@BindsInstance修饰,那么setterMethod方法参数不能使用原始类型;
module节点校验规则
ModuleProcessingStep类对使用MODULE或ProducerModule注解的节点校验,规则如下:
注1:如果componentAnnotation使用的是production类型的注解,那么componentAnnotation#modules里面的module既可以使用ProducerModule也可以使用Module注解;如果componentAnnotation不是使用Production,那么module节点只可以使用Module注解;
注2:当前module节点如果使用了Module注解,那么moduleAnnotation#includes里面的子module节点只能使用Module注解修饰;如果当前module节点使用ProducerModule注解,那么moduleAnnotation#includes里面的子module节点既可以使用ProducerModule注解也可以使用Module注解;
-
module节点只能是类或接口,并且允许使用泛型;
-
module节点不能是Kotlin Companion Object对象;
-
对module节点中的bindingMethod方法校验,自行往下查看;
-
如果module节点中使用了ContributesAndroidInjector注解,那么必须引入androidProcessor;
-
同一个module节点中不允许既使用abstract修饰的bindingMethod方法,又使用非静态的普通实现的bindingMethod方法;
-
module节点和module节点所在父节点,都不允许使用private修饰,最好是public修饰,否则可能存在访问不到bindingMethod方法的情况;
-
同一个module节点中不允许出现同名bindingMethod绑定方法;
-
如果当前module节点不是接口,那么当前module中的bindingMethod方法,既不可以被重写,也不可以重写父级方法;
-
如果module节点使用了泛型,那么当前module节点必须是abstract修饰(接口则不需要);
-
校验moduleAnnotation#includes里面的子module节点,从步骤1开始;
- 注如果当前module节点使用Module注解,那么moduleAnnotation#includes里面的子module节点只能使用Module注解;如果module注解使用ProducerModule注解,那么当前moduleAnnotation#includes里面的子module节点既可以使用Module注解,也可以使用ProducerModule注解;
- moduleAnnotation#subcomponents里面必须是subcomponent节点(使用Subcomponent或productionProduction注解):
-
subcomponent节点中必须存在creator节点;
-
详细subcomponent节点的校验自行查看;
-
module节点不允许使用Scope注解修饰的注解修饰;
-
当前module节点不能存在于moduleAnnotation#includes中-不能存在死循环;
-
如果module节点存在 Kotlin Companion Object对象,对当前Kotlin Companion Object对象里面校验bindingMethod方法(自行查看),并且该bindingMethod方法不是重写方法;
-
以上步骤无错情况下,还可以对当前module节点进行有向图校验,这个步骤极其复杂,后面会介绍到,这里不做讲解。
bindingMethod节点校验规则
BindingMethodProcessingStep类对bindingMethod方法进行校验。
module节点中使用@Provides 、@Produces 、@Binds 、@Multibinds、@BindsOptionalOf注解修饰的方法,称之为bindingMethod绑定方法。
在AnyBindingMethodValidator类中对BindingMethodValidatorsModule中的五个BindingMethodValidator绑定类型校验类执行具体校验如下:
- 根据当前bindingMethod方法是否使用@IntoSet(使用ContributionType.SET表示)、@IntoMap(使用ContributionType.MAP表示)、@ElementsIntoSet(使用ContributionType.SET_VALUES表示)注解或者都没有(使用ContributionType.UNIQUE表示)检查bindingMethod方法返回类型:
-
(1)如果是UNIQUE:那么bindingMethod返回类型必须满足:
-
① 方法返回类型不能使用FrameworkType架构类型:Provider,Lazy,MembersInjector,Produced,Producer;
-
② 在bindingMethod方法没有使用Qualifier修饰的注解修饰情况下,bindingMethod方法的返回类型节点的构造函数不能使用AssistedInject修饰并且返回类型节点不能使用AssistedFactory注解修饰;
-
③ bindingMethod返回类型只能是原始类型或数组或接口或类或变量类型;
-
(2)如果是SET或MAP:那么bindingMethod返回类型只能是原始类型或数组或接口或类或变量类型;
-
(3)如果是SET_VALUES:那么bindingMethod方法返回类型必须是Set< T>,并且T只能是原始类型或数组或接口或类或变量类型;
-
(4)如果bindingMethod使用了@Multibinds修饰,那么不允许使用ContributionType类型,并且bindingMethod返回类型必须是Map<K,V>或Set< V>,并且V不能使用FrameworkType架构类型:Provider,Lazy,MembersInjector,Produced,Producer;
-
bindingMethod方法上最多只允许被一个Qualifier修饰的注解修饰;
-
bindingMethod方法上如果是@BindsOptionalOf和@Multibinds是不允许使用@IntoSet、@IntoMap、@ElementsIntoSet注解的;并且如果是@Provides、@Binds、@Produces三种bindingMethod方法也只能选择@IntoSet、@IntoMap、@ElementsIntoSet中的一个使用,并且@IntoMap和@MapKey修饰的注解一定是成对出现的;
-
bindingMethod方法使用Scope修饰的注解情况,只有@Binds和@Provides修饰的bindingMethod支持使用Scope注解修饰的注解修饰,并且该bindingMethod方法只允许出现一个使用Scope注解修饰的注解修饰;
-
bindingMethod方法如果是@Produces修饰,那么其所在modue节点只能使用ProducerModule注解,如果是@Provides、@Binds、@BindsOptionalOf、@Multibinds修饰的bindingMethod方法,那么所在的父级module节点可以使用Module或ProducerModule注解;
- 如果bindingMethod方法所在父级节点是Kotlin Component Object类型,那么对该父级节点的父级节点作为module节点校验;
-
bindingMethod方法不允许使用泛型,bindingMethod方法不允许使用private修饰;
-
bindingMethod方法如果是@Provides或@Produces修饰,那么当前方法必须使用实现方法;如果方法使用@Binds、@BindsOptionalOf或@Multibinds修饰,那么必须是抽象方法(abstract修饰或接口非default修饰);
-
bindingMethod方法如果使用@Binds、@BindsOptionalOf或@Multibinds修饰,那么该方法不允许throws异常;如果方法使用@Provides修饰允许throws RuntimeException或Error及其子类型的异常;如果方法使用@Produces修饰允许throws Exception或Error及其子类型的异常;
-
对bindingMethod方法参数节点和参数类型节点做依赖校验,针对参数类型剥离RequestKind获得T作为keyType(如果是RequestKind.INSTANCE,keyType就是参数类型),:
-
注1:①BindsOptionalOf或@Multibindings修饰的bindingMethod方法是没有参数的,所以不会进行下面的校验;②@Binds修饰的bindingMethod方法有且仅有一个参数继续下面的校验;
-
注2:如果是@Provides修饰的bindingMethod方法,那么该方法参数不能是Produced< T>或Producer< T>并且继续下面的校验;
-
注释3:如果是@Binds修饰的bindingMethod方法,①如果还使用了@ElementsIntoSet修饰,那么当前方法返回类型必须是Set,而且T必须是当前方法返回类型或其子类;②当前方法参数必须是返回类型的子类,继续下面的校验;
-
(1) 如果方法参数使用@Assisted修饰,那么不继续往下校验;
-
(2) 当前方法参数节点如果使用了Qualifier修饰的注解修饰,那么最多只能存在一个;
-
(3) 如果当前方法参数节点没有使用Qualifier注解修饰,那么当前参数节点的构造函数不允许使用@AssistedInject修饰;
-
(4) 如果当前方法参数节点没有使用Qualifier注解修饰,并且keyType节点使用了@AssistedFactory修饰,那么参数类型要么是T要么是Provider,而不能使用Lazy、Producer或Produced;
-
(5) keyType不允许是通配符格式
-
(6) 如果keyType其实是MembersInjector(必须存在T)类型对T做成员注入校验:
-
① T节点不能使用Qualifier注解修饰的注解修饰;
-
② T类型只能是类或接口,并且如果是泛型,泛型类型只能是类或接口或数组,数组又只能是类或接口或原始类型或数组,不允许出现例如T是List而不是List的情况;
- @Produces修饰的bindingMethod方法,如果出现@Nullable注解,则警告。
规则常用语
根据代码总结出我们能够更好理解的书面语,当然肯定没有那么细致
component校验总结
- component节点上最多只允许使用一个componentAnnotation注解;
- 当然了,不仅仅是component节点,其他节点也是一样,例如module最多只允许使用一个moduleAnnotaiton注解;
-
component最多只允许使用一个内部类creator节点;
-
component中的方法,如果返回类型是subcomponent或subcomponent.creator,那么这个方法最多只允许出现一次;
-
componentAnnotation#dependencies里面的dependency不允许是module节点;
-
如果componentAnnotation使用的是production类型的注解,那么componentAnnotation#modules,里面的module既可以使用ProducerModule和Module注解;如果componentAnnotationAll不是使用Production,那么module节点只可以使用Module注解;
componentMethod校验总结
-
componentMethod方法不可以使用泛型,并且仅仅针对abstract修饰(或方法接口)、非private、非static的(包括从父级类继承过来的)componentMethod方法做校验;
-
如果componentMethod方法返回类型是subcomponent节点,那么componentMethod方法的参数必须是module节点,并且该方法只允许出现一次同一类型的module节点(表示对该module的实例化),并且这里的module节点必须来源该方法返回类型的subcomponent关联的module节点
- 何为subcomponent关联的module节点?①subcomponent#modules的module节点,②条件①的module上的注解moduleAnnotation#includes的module,③条件①和条件②的module及其父级module节点;
-
componentMethod方法返回类型是subcomponent.creator节点,那么componentMethod方法必须无参;
-
componentMethod方法返回类型是subcomponent或subcomponent.creator的情况,最多只会出现一个;
-
如果componentMethod方法返回类型不是subcomponent节点也不是subcomponent.creator节点,那么方法参数最多只能有一个:
-
(1)如果方法无参,并且如果方法返回类型是MembersInjector,那么首先T不允许使用Qualifier修饰的注解修饰,并且T只能是类或接口,如果是泛型,那么泛型里面只允许是类或接口或数组,数组只允许是类或接口或数组;
-
(2)如果方法存在一个参数,该对该方法和方法参数做成员注入校验:componentMethod方法和当前方法参数不能使用Qualifier修饰的注解修饰,并且参数类型只能是类或接口,如果使用泛型,泛型类型只能是类或接口或数组,数组只能是类或接口或数组;
-
(3)如果方法存在一个参数,在返回类型既不是subcomponent也不是subcomponent.creator的情况下,那么返回类型只能是void或返回类型和参数类型一致;
- componentMethod无参,返回类型不是void,作为componentAll的入口方法只能有一个,并且该方法没有被重写。
creator节点总结
-
creatorAnnotation的Builder和Factory对应的是构建者模式和工厂模式,工厂模式就一个factoryMethod方法,该方法传递n个参数用于实例化component或其子类对象;构建者模式有两个方法,分别是setterMethod方法和buildMethod方法,buildMethod方法用于实例化component或其子类对象,setterMethod一次只允许传递一个参数用于传递实例化component或其子类对象需要的参数;
-
creator和creatorMethod都不允许使用泛型,也不允许使用privat修饰;
-
factoryMethod方法和buildMethod方法都不允许使用@BindsInstance修饰;
-
factoryMethod方法参数如果是原始类型,那么该参数必须使用@BindsInstance修饰;setterMethod参数如果是原始类型,那么该参数或者setterMethod方法其中必须使用@BindsInstance修饰,并且不允许方法和方法参数同时使用@BindsInstance修饰;
-
creator节点上最多只能使用一个creatorAnnotation注解。
module校验总结
- module节点修饰类型取决于所在父级节点类型:
-
(1)如果componentAnnotationAll是production类型,那么componentAnnotationAll#modules里面的module节点既可以使用Module注解也可以使用Producer注解;否则module节点只能使用Module注解修饰;
-
(2)module节点是ProducerModule,那么moduleAnnotation#includes中的子module既可以使用Module注解也可以使用Producer注解;否则子module节点只能使用Module注解修饰;
-
module节点可以使用泛型类型,如果使用了泛型,那么当前module节点要么是接口要么是abstract抽象类;但是module节点不能是Kotlin Companion Object类型;如果module节点中存在Kotlin Companion Object类型,该Kotlin Companion Object可以有bindingMethod方法;
-
module节点及其父节点最好使用public修饰,并且同一个module节点中的bindingMethod方法不能即出现abstract修饰的方法又存在非static修饰的实现方法;同一个module节点也不能出现同名的bindingMethod方法;bindingMethod不是重写也不能被重写;
-
module节点不能使用Scope修饰的注解修饰,并且moduleAnnotation#subcomponents里面的subcomponent节点中必须有creator节点;
bindingMethod方法总结
- bindingMethod绑定方法是module节点上使用@Provides 、@Produces 、@Binds 、@Multibinds、@BindsOptionalOf修饰的方法,并且一次只能使用五种中的一种:
-
(1)该方法不允许使用泛型,不允许使用private修饰;
-
(2)@Provides或@Produces修饰的bindingMethod方法必须使用实现方法;@Binds、@BindsOptionalOf或@Multibinds修饰bindingMethod方法必须使用抽象类型(abstract修饰或接口非default修饰的方法);
- bindingMethod方法上使用@IntoSet、@IntoMap、@ElementsIntoSet:
-
(1)如果是@Multibinds和@BindsOptionalOf不能使用这三种类型的绑定;
-
(2)如果是@Provides、@Produces或@Binds只能使用@IntoSet、@IntoMap或@ElementsIntoSet其中的一种:
-
① 如果使用@ElementsIntoSet修饰,那么bindingMethod方法返回类型必须是Set;
-
② @IntoMap和@MapKey修饰的注解一定是成对出现的;
-
只有@Binds和@Provides修饰的bindingMethod支持使用Scope注解修饰的注解修饰,并且当前bindingMethod方法只允许出现一个Scope注解修饰的注解;
-
bindingMethod方法如果是@Produces修饰,那么其所在modue节点只能使用ProducerModule注解;其他四种类型bindingMethod的父级module节点既可以使用Module注解,也可以ProducerModule注解;
-
@Multibinds修饰的bindingMethod方法返回类型要么是Map<K,V>要么是Set< T>。