4.基于Dagger2.38.1版本全面理解注解-component

前言

这里主要针对component(@Component、@ProductionComponent、@Subcomponent、@ProductionSubcomponent)、creator(@Component.Factory、@ProductionComponent.Factory、@Subcomponent.Factory、@ProductionSubcomponent.Factory、@Component.Builder、@ProductionComponent.Builder、@Subcomponent.Builder、@ProductionSubcomponent.Builder)、module(@Module、@ProducerModule)、bindingMethod(@Provides、@Produces、@Binds、@Multibinds、@BindsOptionalOf)、@BindsInstance注解讲解。

component节点

component节点表示使用@Component、@ProductionComponent、@Subcomponent或@ProductionSubcomponent修饰的节点,该系列注解仅仅用于修饰类或接口;subcomponent节点表示使用@Subcomponent或@ProductionSubcomponent修饰的节点。

component节点规则

  1. component节点上有且仅有一个@Component、@ProductionComponent、@Subcomponent或@ProductionSubcomponent注解修饰;

  2. 如果component节点上使用了@CancellationPolicy注解修饰,那么当前component节点只能使用@ProductionSubcomponent或@ProductionComponent注解;

  3. component节点只能是abstract抽象类或接口;

  4. component节点最多只能存在一个creator节点;

  5. component节点不能使用@Reusable修饰;

  6. 如果component是一个kotlin文件,那么component节点中的componentMethod方法名不能使用java关键字;

  7. component使用的注解的dependencies方法不允许收集module节点:

  • e.g.:@Component#dependencies(module.class)错误;

componentMethod表示component节点中的方法,仅仅针对abstract修饰(或接口方法)、非private、非static的(包括从父级类继承过来的)componentMethod方法做校验,规则如下

  1. componentMethod方法返回类型是subcomponent或subcomponent.creator,该方法最多只能出现一次。返回类型是subcomponent和subcomponent.creator不允许同时出现;

  2. componentMethod方法不能使用泛型,如果当前component节点是kotlin文件,那么注意componentMethod不能使用java关键字;

  3. 如果componentMethod方法的returnType返回类型是subcomponent节点:

  • (1)当前componentMethod方法的参数必须是module节点,并且该方法只允许一次同一类型的module节点,并且这个module节点来源于(2)-subcomponent关联的module节点;

  • (2)收集subcomponent关联的module节点:①subcomponentAnnotation#modules里面的module节点;②条件①module节点上的注解moduleAnnotation#includes里面的module节点;③条件①和条件②的module节点的父级module(使用了moduleAnnotation注解)节点;

  1. componentMethod方法返回类型是subcomponent.creator节点,当前方法参数必须为空;

  2. 方法返回类型不是subcomponent节点也不是subcomponent.creator节点,那么参数最多只允许有一个,并且对无参和有一个参数的情况分别校验:

  • (1)componentMethod方法无参,对当前componentMethod方法返回类型做依赖校验,依赖校验自行查看上一章;

  • (2)componentMethod方法返回类型不是subcomponent节点也不是subcomponent.creator节点,并且有且仅有一个参数,该componentMethod方法返回类型要么是void,要么参数类型和方法返回类型一致;

component节点入口方法规整

源码给的是entryPoint,我给直译成入口方法了。

入口方法,表示存在依赖的方法。componentMethod方法返回类型是subcomponent的方法不存在依赖。那么存在依赖的方法有:

  1. 方法返回类型是subcomponent.creator:该方法无参,当前方法返回类型作为依赖去匹配绑定对象:
  • 翻译成大白话就是subcomponent.creator的实例化依赖匹配对象;依赖即表示某某实例化需要外部提供参数对象,参数对应到参数类构造函数(或对应到返回类型是该参数类的bindingMethod方法,或其他)表示匹配;
  1. 方法返回类型既不是subcomponent也不是subcomponent.creator,该方法最多可以有一个参数:
  • 如果该方法有且仅有一个参数,表示需要通过component容器给当前参数表示的类中使用@Inject修饰的变量(或普通方法的参数)实例化

  • 如果方法无参,那么等同于返回类型是subcomponent.creator;

@ProductionComponent或@ProductionSubcomponent注解修饰

如果component使用的是@ProductionComponent或@ProductionSubcomponent注解修饰,那么Dagger会给我们生成一个module类,如下Component类使用了@ProductionComponent或@ProductionSubcomponent注解修饰:

@Module
abstract Component_MonitoringModule{
	private component_MonitoringModule(){}

	@Multibinds
	abstract Set setOfFactories();

	@Provides
	@ProductionScope
	static ProductionComponentMonitor monitor(Provider component,Provider> factories){

		return Monitors.createMonitorForComponent(component, factories);
	}
}

componentAnnotation#dependencies

目前知道的是componentAnnotation#dependencies里面的Dependency节点不允许是module节点。

这个属性显得太偏门,一般用的也比较少,但是这里还是讲解一下。

对(1)Dependency节点以及(2)Dependency无参返回类型不是void的方法:作为匹配对象,该匹配对象没有依赖,说明Dependency节点类型和Dependency无参返回类型不是void的方法返回类型是被component关联的绑定对象实例化过了。

没有依赖,就不会往下去匹配。

creator节点 和 @BindsInstance

使用@Component.Factory、@ProductionComponent.Factory、@Subcomponent.Factory、@ProductionSubcomponent.Factory、@Component.Builder、@ProductionComponent.Builder、@Subcomponent.Builder、@ProductionSubcomponent.Builder修饰的节点,creator节点是component节点的内部类。

creator节点规则如下

  1. creator节点上的creatorAnnotation注解最多只能使用一个;

  2. creator节点所在的父级节点一定是component节点;

  3. creator节点只能是类或接口,并且如果是类的话,该类构造函数只能使用默认的;

  4. creator节点不能使用泛型,并且creatorMethod不能使用private修饰;

  5. 如果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修饰 || 要么不使用原始类型;

  1. 如果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修饰;

factory表示的是工厂模式,builder表示的是建造者模式,可以自行对照这两个模式去理解下。

@BindsInstance规则如下

@BindsInstance只允许修饰方法或方法参数

  1. @BindsInstance修饰方法参数:
  • (1)当前使用@BindsInstance修饰的方法参数:

  • ① 该参数不能使用FrameworkType架构类型:Provider,Lazy,MembersInjector,Produced,Producer;

  • ② 参数类型只能是原始类型或数组或接口或类或变量类型;

  • (2)参数节点不能使用@Scope修饰的注解修饰;

  • (3)参数所在方法必须是abstract修饰的抽象方法或接口中的非default方法;

  • (4)@BindsInstance修饰的参数所在方法的返回类型,只能是一个类或接口(可以是泛型),不能是void、数组又或者原始类型

  1. @BindsInstance修饰方法:
  • (1)当前使用@BindsInstance修饰的方法校验:

  • 注:@BindsInstance修饰的方法有且仅有一个参数;该参数和@BindsInstance修饰方法参数规则一致;

  1. @BindsInstance修饰方法(或方法参数所在方法)的父级节点只能是creator节点;

也就是说**@BindsInstance修饰方法和方法参数区别在于修饰方法那么当前方法有且仅有一个参数,修饰方法参数那么当前方法参数不受限制**。

module节点

module节点表示使用@Module或@ProducerModule修饰的节点。规则如下:

  1. module节点修饰类型取决于所在引用注解类型:
  • 注:哪些注解引用module节点:①componentAnnotation#modules;②moduleAnnotation#includes;

  • (1)如果componentAnnotation是production类型,那么componentAnnotation#modules里面的module节点既可以使用Module注解也可以使用ProducerModule注解;否则module节点只能使用Module注解修饰;

  • (2)module节点是ProducerModule,那么moduleAnnotation#includes中的子module既可以使用Module注解也可以使用ProducerModule注解;否则子module节点只能使用Module注解修饰;

  1. module节点可以使用泛型类型,如果使用了泛型,那么当前module节点要么是接口要么是abstract抽象类;但是module节点不能是Kotlin Companion Object类型;如果module节点中存在Kotlin Companion Object类型,该Kotlin Companion Object可以有bindingMethod方法;

  2. module节点及其父节点最好使用public修饰,并且同一个module节点中的bindingMethod方法不能即出现abstract修饰的方法又存在非static修饰的实现方法;同一个module节点也不能出现同名的bindingMethod方法;bindingMethod不是重写也不能被重写;

  3. module节点不能使用@Scope修饰的注解修饰,并且moduleAnnotation#subcomponents里面的subcomponent节点中必须有creator节点;

bindingMethod节点

  1. bindingMethod绑定方法是module节点上使用@Provides 、@Produces 、@Binds 、@Multibinds、@BindsOptionalOf修饰的方法,并且一次只能使用五种中的一种:
  • (1)该方法不允许使用泛型,不允许使用private修饰;

  • (2)@Provides或@Produces修饰的bindingMethod方法必须使用实现方法;@Binds、@BindsOptionalOf或@Multibinds修饰bindingMethod方法必须使用抽象类型(abstract修饰或接口非default修饰的方法);

  1. bindingMethod方法上使用@IntoSet、@IntoMap、@ElementsIntoSet:
  • (1)如果是@Multibinds和@BindsOptionalOf不能使用这三种类型的绑定;

  • (2)如果是@Provides、@Produces或@Binds只能使用@IntoSet、@IntoMap或@ElementsIntoSet其中的一种:

  • ① 如果使用@ElementsIntoSet修饰,那么bindingMethod方法返回类型必须是Set;

  • ② @IntoMap和@MapKey修饰的注解一定是成对出现的;

  1. 只有@Binds和@Provides修饰的bindingMethod支持使用@Scope注解修饰的注解修饰,并且当前bindingMethod方法只允许出现一个@Scope注解修饰的注解;

  2. bindingMethod方法如果是@Produces修饰,那么其所在module节点只能使用ProducerModule注解;其他四种类型bindingMethod的父级module节点既可以使用Module注解,也可以ProducerModule注解;

  3. @Multibinds修饰的bindingMethod方法返回类型要么是Map要么是Set< T>。

Dagger注解的依赖和匹配

从componentMethod入口方法开始,该方法的依赖去匹配绑定对象,绑定对象的依赖又去匹配其他绑定对象…直到当前绑定对象没有依赖。换个意思理解一下,从componentMethod方法开始,该方法的返回类型(或参数表示的类中使用@Inject修饰变量或普通方法的参数)的实例化需要依赖它的构造函数生成的绑定对象(或者其他能找到和他类型一致的绑定对象),该绑定对象的实例化参数需要依赖于其他绑定对象…直到当前绑定对象不存在依赖那说明:(1)当前绑定对象可以直接实例化,又或者(2)该绑定对象是通过component容器外部实例化传递进来的(@BindsInstance修饰的目的就在于此)。

我反正是理解了,或者说的感觉过于啰嗦,我们下面来看看入口方法、绑定对象,哪些绑定对象存在依赖。

入口方法依赖

  1. componentMethod无参,该方法返回类型作为依赖去匹配;

  2. componentMethod返回类型不是subcomponent、有且仅有一个参数,该参数类型:对该参数类型表示的类中使用@Inject修饰的变量(或普通方法参数)类型的实例化-作为依赖去匹配。

绑定对象(是否存在依赖,存在依赖就存在匹配工作)

  1. component节点生成绑定对象:component节点用于被匹配,不存在依赖;

  2. componentAnnotation#dependencies中的dependency节点生成绑定对象:dependency节点用于被匹配,不存在依赖;

  3. componentAnnotation#dependencies中的dependency节点中无参 && 返回类型不是void的方法生成绑定对象:当前方法返回类型用于被匹配,不存在依赖;

  4. component.creator中被@BindsInstance修饰过的方法或方法参数生成绑定对象:方法参数用于被匹配,不存在依赖的原因是当前参数是component容器外部实例化传递进来的实例;

  5. componentMethod返回类型是subcomponent.creator(这里的creator只能是Builder类型,并且该subcomponent不在component关联的subcomponent名单中)生成的绑定对象:subcomponent.creator用于被匹配,不存在依赖;

  6. bindingMethod是@Provides或@Produces修饰的方法生成的绑定对象(及其常见):方法返回类型用于被匹配;方法参数表示依赖,去匹配其他依赖对象;

  7. bindingMethod是@Binds修饰的方法生成的绑定对象(及其常见):方法返回类型用于被匹配;方法参数表示依赖,去匹配其他依赖对象;

  8. bindingMethod是@Multibinds修饰的方法生成的绑定对象(主要是收集工作):方法返回类型用于被匹配,匹配上即被作为同一种类型收集起来;不需要去依赖;

  9. componentAnnotation#subcomponents中的subcomponent节点生成绑定对象:subcomponent.creator用于被匹配;不需要依赖;

  10. @BindsOptionalOf修饰的bindingMethod方法生成的绑定对象:当前方法返回类型用于被匹配;不需要依赖;

  11. 如果依赖类型是MembersInjector< T>,那么对当前T中使用@Inject修饰的变量或普通方法参数:@Inject修饰的变量或普通方法参数同样达到被当前component容器实例化的效果:

  • (1)依赖类型是MembersInjector< T>用的不多;

  • (2)依赖类型是MembersInjector< T>哪些情况:①componentMethod方法无参,返回类型是MembersInjector< T>,T生成MembersInjectionBinding;②componentMethod返回类型既不是subcomponent又不是subcomponent.creator,有且仅有一个参数,当前参数生成MembersInjectionBinding(正常用法);③module节点中的被@Produces或@Provides或@Binds修饰的bindingMethod方法中的参数(其中@Binds修饰的方法有且仅有一个参数)类型是MembersInjector< T>,T生成MembersInjectionBinding;;④Inject或AssistedInject修饰的构造函数的参数如果使用了MembersInjector< T>,T生成MembersInjectionBinding;⑤Inject修饰的变量(或普通方法的参数)类型是MembersInjector< T>,T生成MembersInjectionBinding;

  1. @AssistedFactory修饰节点生成的绑定对象:@AssistedFactory修饰的节点用于被匹配,节点中唯一的方法的返回类型作为依赖,该依赖去匹配@AssistedInject修饰的构造函数;

  2. @Inject或@AssistedInject修饰的构造函数生成的绑定对象:该构造函数类型用于被匹配;该构造函数的参数作为依赖(@Assisted修饰的参数是直接传递的参数,所以不需要依赖);

  3. @IntoSet、@IntoMap和@ElementsIntoSet结合@Binds、@Provides或@Produces使用,表示的是收集工作。

所有的注解应该都解释到位了吧。我感觉比较满意。

总结

解说完毕,但是国内好像对Dagger并不是很热衷,国外火得很,hilt还有核心Dagger代码实现还需要进一步去整理,这里game over了~。

可在QQ群:575306647 讨论

源码解析github地址

你可能感兴趣的:(源码实际应用,android)