Dagger2解析5-Qualifier
Dagger2系列:
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
中的两个成员变量memberA
和memberB
遇到点小坑,之前直接这么写的
@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[]里,不然无效