美文网首页
Dagger2解析5-Qualifier

Dagger2解析5-Qualifier

作者: 大空ts翼 | 来源:发表于2017-11-30 14:50 被阅读39次

    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[]里,不然无效

    相关文章

      网友评论

          本文标题:Dagger2解析5-Qualifier

          本文链接:https://www.haomeiwen.com/subject/xbusbxtx.html