Dagger2解析5-Qualifier

2017-11-30  本文已影响39人  大空ts翼

Dagger2系列:

  1. Dagger2解析-1
  2. Dagger2解析2-Component的依赖关系
  3. Dagger2解析3-SubComponent
    4.Dagger2解析4-Scope

Dagger版本:2.11

在第二篇Dagger2解析2-Component的依赖关系中曾经提到过,一个组件中依赖关系中不能有相同类型的提供者(构造函数标注@Inject的可创建实例的对象或者module里标注@Provides的方法),否则会出现依赖迷失的错误,而编译时判断的依据就是类型是否重复,可是有些时候就是会出现类型相同的不同实例时又该如何解决呢,这里就需要用到本篇的主角Qualifier(限定符)了

通过限定符,可以从多个提供相同类型的Provider中找出正确的那个

老样子,先看Qualifier类

package javax.inject;
// ......
/**
 *Identifies qualifier annotations. Anyone can define a new qualifier. A
 * qualifier annotation:
 * ......
 */
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Qualifier {}

Qualifier同样是javax.inject包下的一个注解类,和Scope类似,也是能够自定义的

1.示例

代码撸起,还是老朋友Target和Member,这次Module会有多个提供Member的方法

class Member(val name: String)

class Target {

    @field:[Inject CustomQualifier(name = "A")]
    lateinit var memberA: Member

    @field:[Inject CustomQualifier(name = "B")]
    lateinit var memberB: Member
}

@Module
class MemberModule {
    @CustomQualifier(name = "A")
    @Provides
    fun provideMemberA(): Member = Member("by provideMemberA")

    @CustomQualifier(name = "B")
    @Provides
    fun provideMemberB(): Member = Member("by provideMemberB")
}

@Component(modules = arrayOf(MemberModule::class))
interface TargetComponent {
    fun inject(target: Target)
}

ps:用java的无视就好,关于Target中的两个成员变量memberAmemberB遇到点小坑,之前直接这么写的

    @Inject 
    @CustomQualifier(name = "A")]
    lateinit var memberA: Member

    @Inject 
    @CustomQualifier(name = "B")]
    lateinit var memberB: Member

一直编译不通过,提示找不到能提供实例的provider,后来在stackoverflow上找到一篇解答,才发现kotlin里属性的标注要用@field:[/* 和成员变量相关的注解放在这 */]

2. 代码分析

由于例子简单,和 Dagger2解析-1的相比也不过就多了一个工厂,事实上,这两个工厂的代码也是几乎一样的,唯一不同的是get方法调用的module对应的两个provide方法

public final class MemberModule_ProvideMemberAFactory implements Factory<Member> {
  //...
  @Override
  public Member get() {
    return Preconditions.checkNotNull(
        // 调用的是module的provideMemberA
        module.provideMemberA(), "Cannot return null from a non-@Nullable @Provides method");
  }
  //...
}

public final class MemberModule_ProvideMemberBFactory implements Factory<Member> {
  // ...
  @Override
  public Member get() {
    return Preconditions.checkNotNull(
        // 调用的是module的provideMemberB
        module.provideMemberB(), "Cannot return null from a non-@Nullable @Provides method");
  }
  // ...
}

然后是Injector

public final class Target_MembersInjector implements MembersInjector<Target> {
  private final Provider<Member> memberAProvider;
  private final Provider<Member> memberBProvider;
  //...
  @Override
  public void injectMembers(Target instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    // complier生成代码时通过限定符注解成功找到了对应的提供依赖的provider
    instance.memberA = memberAProvider.get();
    instance.memberB = memberBProvider.get();
  }
}

可以看到,代码上并没有什么奇怪的操作,所以说明这个注解提供给编译器生成代码后就没啥用了,其他的也没啥好说的了,通过对module和需要被注入的依赖上标注限定符,编译器在发现重复的Provider时,就能通过限定符去寻找正确的Provider了

ps1:关于注解不太明白,retention保留的问题,照实现原理看,这些注解,包括@Component @Module @Scope以及本篇的@Qualifier都应该不需要在运行期保留啊,但写法都是@Retention(RetentionPolicy.RUNTIME)?望大佬指明
ps2:关于kotlin的注解类annotation class,它的Retention默认就是RUNTIME,所以可以不写,例如本篇中的CustomQualifier可以写成

@Qualifier
annotation class CustomQualifier(val name: String = "")

3.总结

1.这个注解是用来解决依赖迷失的,适合注入对象需要多个同类型依赖的场合,视情况个人觉得Scope也能拿来解决这个问题
2.和前面的注解不同,kotlin上用要注意把相关注解写在@field[]里,不然无效

上一篇下一篇

猜你喜欢

热点阅读