商业转载请联系作者获得授权,非商业转载请注明出处。
目录
一 Dagger2
1.1 简介:
1.2 起源
二 Dagger2注解初识
2.1 @Inject:
2.2 @Module:
2.3 @Provides:
2.4 @Component:
2.5 @Qulifier:
2.6 @Scope:
2.7 @Singleton:
三 Dagger2使用
3.1 引入Dagger2
3.2 案例A:入门demo
3.3 案例B
3.3.1 @Module、@Provide 的使用
3.3.2 Dagger2 的依赖查找顺序说明
3.4 案例C :@Qualifiers 和 @Name的使用
3.4.1 @Qulifier注解
3.4.2 @Name注解
四 进阶使用之单例
4.1 @Singleton实现的局部单例
4.1.1 创建UserBean对象
4.1.2 创建Module
4.1.3 创建Component
4.1.4 UserActivity中进行测试
4.2 自定义@Scope注解实现的局部单例
4.3 全局单例
4.3.1 创建UserBean类
4.3.2 创建BaseModule
4.3.3 创建BaseComponent
4.3.4 在application中提供唯一的baseComponent类
4.3.5 自定义一个Scope
4.3.6 创建UserModule
4.3.7 创建UserComponet
4.3.8 activity中测试
补充1
参考文章:
一 Dagger2
1.1 简介:
1:学习Dagger2需要注解、反射相关的知识。推荐阅读:
《Java内功修炼系列:依赖倒置、控制反转、依赖注入》
《Java内功修炼系列:注解(Annotation)》
《Java内功修炼系列:Java反射入门进阶到使用》
2:Dagger2起源于Dagger,是一款基于Java注解来实现的完全在编译阶段完成依赖注入的开源库。主要用于模块间解耦、提高代码的健壮性和可维护性。
3:Dagger2在编译阶段通过apt利用Java注解自动生成Java代码,然后结合手写的代码来自动帮我们完成依赖注入的工作。
4:Dagger2 需要先编译一次代码,不然就会报错。编译代码是为了生成中间代码,然后在中间代码的基础上按照正常的流程开发。
5:Dagger2 原理利用了反射,而反射处理相对于正常开发速度很慢。Dagger2 把反射处理移动到编译器编译代码时的阶段,而程序运行时并不涉及到反射(补充1):它在编译时产生一些新的代码,然后再在程序运行过程中正常使用。所以这就是Dagger2,或者其他类似于 GreenDao、Butterknife等三方库运用到了反射,却仍然高效的秘诀。
6:Dagger2 是基于注解开发的,而 Dagger2 中所涉及到的注解其实是基于 javax.inject 上开发的,它出自 JSR330。JSR330 是规范,建议大家怎么做,而 Dagger2 则实现了这个规范,对于普通开发者而言,学习 Dagger2 其实就是学习相关注解的意义与用途。
1.2 起源
起初Square公司受到Guice的启发而开发了Dagger,但是Dagger这种半静态半运行时的框架还是有些性能问题(虽说依赖注入是完全静态的,但是其有向无环图(Directed Acyclic Graph)还是基于反射来生成的,这无论在大型的服务端应用还是在Android应用上都不是最优方案)。因此Google工程师Fork了Dagger项目,对它进行了改造。于是变演变出了今天我们要讨论的Dagger2,所以说Dagger2其实就是高配版的Dagger。
二 Dagger2注解初识
Dagger2是基于Java注解来实现依赖注入的,那么在正式使用之前我们需要先了解下Dagger2中的注解。Dagger2使用过程中我们通常接触到的注解主要包括:@Inject, @Module, @Provides, @Component, @Qulifier, @Scope, @Singleten。
2.1 @Inject:
@Inject有两个作用:
一是用来标记需要依赖的变量,以此告诉Dagger2为它提供依赖;
二是用来标记构造函数,Dagger2通过@Inject注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被@Inject标记了的变量提供依赖;
对应请看下面的 案例A 加深学习。
2.2 @Module:
@Module用于标注提供依赖的类。
你可能会有点困惑,上面不是提到用@Inject标记构造函数就可以提供依赖了么,为什么还需要@Module?
比如,很多时候我们需要提供依赖的构造函数是第三方库的,我们没法给它加上@Inject注解;
又比如,需要被注入的依赖类提供的构造函数是带参数的,那么他的参数又怎么来呢?
@Module正是帮我们解决这些问题的。
对应请看下面的 案例B 加深学习。
2.3 @Provides:
@Provides用于标注Module所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值;
对应请看下面的 案例B 加深学习。
2.4 @Component:
@Component用于标注接口,是依赖需求方和依赖提供方之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类(如果@Component标注的接口为CarComponent,则编译期生成的实现类为DaggerCarComponent),我们通过调用这个实现类的方法完成注入;
2.5 @Qulifier:
@Qulifier用于自定义注解,也就是说@Qulifier就如同Java提供的几种基本元注解一样用来标记注解类。我们在使用@Module来标注提供依赖的方法时,方法名我们是可以随便定义的(虽然我们定义方法名一般以provide开头,但这并不是强制的,只是为了增加可读性而已)。
那么Dagger2怎么知道这个方法是为谁提供依赖呢?答案就是返回值的类型,Dagger2根据返回值的类型来决定为哪个被@Inject标记了的变量赋值。但是问题来了,一旦有多个一样的返回类型Dagger2就懵逼了。@Qulifier的存在正式为了解决这个问题,我们使用@Qulifier来定义自己的注解,然后通过自定义的注解去标注提供依赖的方法和依赖需求方(也就是被@Inject标注的变量),这样Dagger2就知道为谁提供依赖了。详细可看下面的案例C进行理解。
一个更为精简的定义:当类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示;
2.6 @Scope:
@Scope同样用于自定义注解,我能可以通过@Scope自定义的注解来限定注解作用域,实现单例(分局部和全局);
@Scope需要Component和依赖提供者配合才能起作用,对于@Scope注解的依赖,Component会持有第一次创建的依赖,后面注入时都会复用这个依赖的实例,实质上@Scope的目的就是为了让生成的依赖实例的生命周期与 Component 绑定
如果Component重建了,持有的@Scope的依赖也会重建,所以为了维护局部单例需要自己维护Component的生命周期。
2.7 @Singleton:
@Singleton其实就是一个通过@Scope定义的注解,我们一般通过它来实现全局单例。但实际上它并不能提前全局单例,是否能提供全局单例还要取决于对应的Component是否为一个全局对象。
三 Dagger2使用
3.1 引入Dagger2
Dagger2 官网地址是 https://google.github.io/dagger/,它是应于 Java 和 Android 开发的依赖注入框架,而不单单是Android。
dependencies {
api 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}
3.2 案例A:入门demo
Car类是需求依赖方,依赖了Engine类;因此我们需要在类变量Engine上添加@Inject来告诉Dagger2来为自己提供依赖。 Engine类是依赖提供方,因此我们需要在它的构造函数上添加@Inject
public class Engine {
/**
* Dagger2通过@Inject注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,
* 以此来为被@Inject标记了的变量提供依赖
*
*/
@Inject
public Engine() {
}
@NonNull
@Override
public String toString() {
return "Engine{}";
}
public void run() {
Log.i("tag", "引擎转起来了~~~ ");
}
接下来我们需要创建一个用@Component标注的接口CarComponent,这个CarComponent其实就是一个注入器,这里用来将Engine注入到Car中。
@Component
public interface CarComponent {
void inject(Car car);
}
完成这些之后我们需要Build下项目,让Dagger2帮我们生成相关的Java类。接着我们就可以在Car的构造函数中调用Dagger2生成的DaggerCarComponent来实现注入(这其实在前面Car类的代码中已经有了体现)
public class Car {
/**
* @Inject:@Inject有两个作用,一是用来标记需要依赖的变量,以此告诉Dagger2为它提供依赖
*/
@Inject
Engine engine;
public Car() {
DaggerCarComponent.builder().build().inject(this);
}
public Engine getEngine() {
return this.engine;
}
public static void main(String ... args){
Car car = new Car();
System.out.println(car.getEngine());
}
}
3.3 案例B
3.3.1 @Module、@Provide 的使用
如果创建Engine的构造函数是带参数的呢?或者Eggine类是我们无法修改的呢?这时候就需要@Module和@Provide上场了。
可以看到下面Engine类的代码和上面的入门demo中的Engine代码几乎一样,只是多了个带参数的构造方法。
public class Engine {
private String name;
@Inject
Engine(){}
Engine(String name) {
this.name = name;
}
@Override
public String toString() {
return "Engine{" +
"name='" + name + '\'' +
'}';
}
public void run() {
System.out.println("引擎转起来了~~~");
}
}
接着我们需要一个Module类来生成依赖对象。前面介绍的@Module就是用来标准这个类的,而@Provide则是用来标注具体提供依赖对象的方法(这里有个不成文的规定,被@Provide标注的方法命名我们一般以provide开头,这并不是强制的但有益于提升代码的可读性)。
@Module
public class MarkCarModule {
String engineType;
public MarkCarModule(String engineType) {
this.engineType = engineType;
}
@Provides
Engine provideEngine() {
return new Engine(engineType);
}
}
接下来我们还需要对CarComponent进行一点点修改,之前的@Component注解是不带参数的,现在我们需要加上modules = {MarkCarModule.class},用来告诉Dagger2提供依赖的是MarkCarModule这个类。
@Component(modules = MarkCarModule.class)
public interface CarComponent {
void inject(Car car);
}
Car类的构造函数我们也需要修改,相比之前多了个markCarModule(new MarkCarModule())方法,这就相当于告诉了注入器DaggerCarComponent把MarkCarModule提供的依赖注入到了Car类中。
public class Car {
@Inject
Engine engine;
public Car() {
DaggerCarComponent.builder().markCarModule(new MarkCarModule("国产发动机"))
.build().inject(this);
}
public Engine getEngine() {
return this.engine;
}
public static void main(String ... args){
Car car = new Car();
System.out.println(car.getEngine());
}
}
这样一个最最基本的依赖注入就完成了,接下来我们测试下我们的代码。 输出:
Engine{name='国产发动机'}
3.3.2 Dagger2 的依赖查找顺序说明
我们提到@Inject和@Module都可以提供依赖,那如果我们既在构造函数上通过标记@Inject提供依赖,又通过@Module提供依赖Dagger2会如何选择呢?具体规则如下:
- 步骤1:首先查找@Module标注的类中是否存在提供依赖的方法。
- 步骤2:若存在提供依赖的方法,查看该方法是否存在参数。
- a:若存在参数,则按从步骤1开始依次初始化每个参数;
- b:若不存在,则直接初始化该类实例,完成一次依赖注入。
- 步骤3:若不存在提供依赖的方法,则查找@Inject标注的构造函数,看构造函数是否存在参数。
- a:若存在参数,则从步骤1开始依次初始化每一个参数
- b:若不存在,则直接初始化该类实例,完成一次依赖注入。
总结:Dagger2 依赖查找的顺序是先查找 Module 内所有的 @Provides 提供的依赖,如果查找不到再去查找 @Inject 提供的依赖。
3.4 案例C :@Qualifiers 和 @Name的使用
在同一个 Module 中 ,@Provides 提供的依赖是由返回值决定的。这样就会出现问题,同一种类型不同实例,怎么去区别?比如如果一台汽车有两个引擎(也就是说Car类中有两个Engine变量),3.3.1案例B中的MarkCarModule你可能就会这么写:
@Module
public class MarkCarModule {
public MarkCarModule(){ }
@Provides
Engine provideEngineA(){
return new Engine("国产发动机");
}
@Provides
Engine provideEngineB(){
return new Engine("德国发动机");
}
}
大家应该会这样编码,但是根本编译不过。因为 Dagger2 是根据返回的类型来进行依赖关系确定的。如果存在两个方法返回一样的类型,那么正常情况下 Dagger2 没法知道选哪个(比如上述代码中,不知道选provideEngineA()方法还是provideEngineB()方法)。
Dagger2 给出了解决方案: @Name 注解 或者 @Qulifier注解:
3.4.1 @Qulifier注解
我们通过@Qulifier注解来自定义两个注解,用于区分返回类型相同但又是不同对象的两个具体类,在例子里就是国产发动机和德国发动机。
public class Engine {
/**
* 使用@Qulifier定义两个注解
*/
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierA { }
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierB { }
private String name;
Engine(String name) {
this.name = name;
}
@Override
public String toString() {
return "Engine{" +
"name='" + name + '\'' +
'}';
}
public void run() {
System.out.println("引擎转起来了~~~");
}
}
同时我们需要对依赖提供方做出修改,用定义的不同的注解来给不同的provideEngine方法贴上标签,这样Dagger2在寻找同类型对象时,就知道到底应该找哪一个了。
@Module
public class MarkCarModule {
public MarkCarModule(){ }
@Engine.QualifierA
@Provides
Engine provideEngineA(){
return new Engine("国产发动机");
}
@Engine.QualifierB
@Provides
Engine provideEngineB(){
return new Engine("德国发动机");
}
}
接下来依赖需求方Car类同样需要修改,需要在注入对象前加上自定义的注解便签,以便Dagger2知道选择哪个provide方法:
public class Car {
@Engine.QualifierA
@Inject
Engine engineA;
@Engine.QualifierB
@Inject
Engine engineB;
public Car() {
DaggerCarComponent.builder().markCarModule(new MarkCarModule())
.build().inject(this);
}
public Engine getEngineA() {
return this.engineA;
}
public Engine getEngineB() {
return this.engineB;
}
public static void main(String... args) {
Car car = new Car();
System.out.println(car.getEngineA());
System.out.println(car.getEngineB());
}
}
3.4.2 @Name注解
看下Named标签的源码:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
可以看到:@Name 实际上也是 @Qualifier 注解的一个注解。原理还是 @Qualifier,只不过往上封装了一层使用起来可能更加方便。
@Module
public class MarkCarModule {
public MarkCarModule(){ }
@Provides
@Named("QualifierA")
Engine provideEngineA(){
return new Engine("国产发动机");
}
@Provides
@Named("QualifierB")
Engine provideEngineB(){
return new Engine("德国发动机");
}
}
public class Car {
@Named("QualifierA")
@Inject
Engine engineA;
@Named("QualifierB")
@Inject
Engine engineB;
public Car() {
DaggerCarComponent.builder().markCarModule(new MarkCarModule())
.build().inject(this);
}
public Engine getEngineA() {
return this.engineA;
}
public Engine getEngineB() {
return this.engineB;
}
public static void main(String... args) {
Car car = new Car();
System.out.println(car.getEngineA());
System.out.println(car.getEngineB());
}
}
四 进阶使用之单例
4.1 @Singleton实现的局部单例
利用Dagger2我们可以舍弃以往双重检查锁等常见的繁琐的单例创建方式。有关单例的学习可以参考这边文章:单例模式学习与进阶。
Dagger2中提供了@Singleton注解来实现单例,实现单例需要两步 :
1 : 在Module对应的Provides方法标明@Singleton,表明提供的是一个单例对象。
2 : 同时在Component类标明@Singleton,表明该Component中有Module使用了@Singleton。
下面我们来写一个简单的局部单例的例子:
4.1.1 创建UserBean对象
public class UserBean {
private String name;
public UserBean(String name) {
this.name = name;
}
}
4.1.2 创建Module
利用@Singleton来提供一个单例对象:
@Module
public class UserModule {
@Singleton
@Provides
UserBean providesUserA() {
return new UserBean("Lucas");
}
}
4.1.3 创建Component
注意:如果 moudule所依赖的Comonent 中有被@Singleton单例的对象,那么Conponnent也必须是单例的@Singleton,表明该Component中有Module使用了@Singleton
@Singleton
@Component(modules = UserModule.class)
public interface UserComponent {
void join(UserActivity userActivity);
}
这里大家可能会有疑问,为什么要用 @Singleton 同时标注 @Provides 和 @Component ?
Component 是联系需求与依赖的纽带,对于@Scope(/@Singleton)注解的依赖,Component会持有第一次创建的依赖,后面注入时都会复用这个依赖的实例,如果Component重建了,持有的@Scope(/@Singleton)的依赖也会重建,所以需要@Scope(/@Singleton)来标注Component。标注@Provides是为了提供唯一的userBean对象。 你甚至可以理解成单例的单例,这么说不严谨,但是有助于理解:我要单例的userBean,所以Component得是个单例,同activity拿到的UserComponent得是同一个具体实现类。同样对provide方法而言,userModule也是UserComponent中的依赖,同一个UserComponent中的userModule也必须是单例。
4.1.4 UserActivity中进行测试
public class UserActivity extends AppCompatActivity {
private TextView textView;
@Inject
UserBean userBeanA;
@Inject
UserBean userBeanB;
@Inject
UserBean userBeanC;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monkey);
textView = findViewById(R.id.textView);
DaggerUserComponent.create().join(this);
}
public void checkSingleton(View view) {
textView.setText("userBeanA:" + userBeanA.hashCode() + "\n"
+ "userBeanB:" + userBeanB.hashCode() + "\n"
+ "userBeanC:" + userBeanC.hashCode() + "\n");
}
}
运行后可以看到,userBeanA、userBeanB、userBeanC三者的hashCode值都是一样的,证明了在此activity中,我们刚刚所写的单例,成功!
值得注意的是:这里的单利对象只能在同一个Activity中有效。
解释一下:比如上面的这个例子,关键在于UserActivity中的 DaggerUserComponent.create().join(this) 这一行,它创建了桥梁UserComponent,所以在这个activity中,所有的userBean对象都是通过这个UserComponent去注入的,而UserComponent已经知道了UserModule是个被@Singeton注解修饰的单例。如果再新建另外一个UserActivity2,并在这个新建的activity2中再DaggerUserComponent.create().join(this) 一下,那么就会产生新的桥梁:UserComponent2(UserComponent是个接口,在不同的activity中传入不同的activity实例,由此也就产生了不同的UserComponent具体实现),这时的userBean对象就不是刚刚那个UserActivity中的userBean了!
不同的Activity 持有的对象不同,使用@Singeton注解或者定自定义的@Scope的, 只能在同一个activity(或者fragment)的一个生命周期中保持单例。
4.2 自定义@Scope注解实现的局部单例
我们看下@Singeton注解的具体实现:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
可以看到, @Singleton 只是 @Scope 一个默认的实现而已,但是因为它更具可读性,能够让开发者一眼就明白它的作用是为了单例。那么我们现在用@Scope来实现一个单例,首先我们需要通过@Scope定义一个CarScope注解:
public class Engine {
/**
* 1. @Scope定义一个CarScope注解
*/
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface CarScope {
}
private String name;
Engine(String name) {
this.name = name;
System.out.println("Engine create: " + name);
}
@Override
public String toString() {
return "Engine{" +
"name='" + name + '\'' +
'}';
}
public void run() {
System.out.println("引擎转起来了~~~");
}
}
接着我们需要用这个@CarScope去标记依赖提供方MarkCarModule,表明这个provide方法提供的对象是单例。
@Module
public class MarkCarModule {
public MarkCarModule(){ }
/**
* 2. @CarScope去标记依赖提供方MarkCarModule
* @return
*/
@Engine.CarScope
@Provides
Engine provideEngine(){
return new Engine("国产发动机");
}
}
同时还需要使用@Scope去标注注入器Compoent,表明该Component中有Module使用了这个自定义的@Scope:
/**
* 3. 同时还需要使用@Scope去标注注入器Compoent
*/
@Engine.CarScope
@Component(modules = MarkCarModule.class)
public interface CarComponent {
void inject(Car car);
}
public class Car {
@Inject
Engine engineA;
@Inject
Engine engineB;
public Car() {
DaggerCarComponent.builder().markCarModule(new MarkCarModule())
.build().inject(this);
}
public Engine getEngineA() {
return this.engineA;
}
public Engine getEngineB() {
return this.engineB;
}
public static void main(String... args) {
Car car = new Car();
System.out.println(car.getEngineA());
System.out.println(car.getEngineB());
}
}
如果我们不使用@Scope,上面的代码会实例化两次Engine类,因此会有两次"Create Engine"输出。现在我们在有@Scope的情况测试下劳动成果: 输出
Engine create: 国产发动机
Engine{name='国产发动机'}
Engine{name='国产发动机'}
4.3 全局单例
上面的局部单例例子中,DaggerUserComponent在两个activity中被各自被实例化一次, 因此产生了两个不同的对象, 我们需要做到让Component能够实现单例,Android中, 我们知道在整个App生命周期中都只有一个Appclication实例,所以在Application中获得一个唯一的component实例,用它来提供我们需要的单例:
4.3.1 创建UserBean类
public class UserBean {
}
4.3.2 创建BaseModule
和局部单例是一样的。
@Module
public class BaseModule {
@Singleton
@Provides
UserBean providesUser() {
return new UserBean();
}
}
4.3.3 创建BaseComponent
这个component是用来让别的component来依赖的, 只需要告诉别的component他可以提供哪些类型的依赖即可。
@Singleton
@Component(modules = BaseModule.class)
public interface BaseComponent {
UserBean getSingletonUser();
}
4.3.4 在application中提供唯一的baseComponent类
public class MyApplication extends Application {
private BaseComponent baseComponent;
@Override
public void onCreate() {
super.onCreate();
baseComponent = DaggerBaseComponent.create();
}
public BaseComponent getBaseComponent() {
return baseComponent;
}
}
4.3.5 自定义一个Scope
4.3.7 中需要创建的UserComponet是需要依赖BaseComponent的,而BaseComponent又是被@singleton标注的,所以UserComponet也需要被@singleton标注,但是已经被@singleton标注过的依赖,不能被被依赖方(下面的UserComponet)使用了,只能自定义一个。
/**
* Scope 标注是Scope
* @Retention(RUNTIME) 运行时级别
*/
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface BaseScope {
}
4.3.6 创建UserModule
这里写的是自定义业务。
@Module
public class UserModule {
}
4.3.7 创建UserComponet
在我们自己的Component, 使用dependencies依赖于baseComponent, (在@Component注解参数中, 可以依赖多个module和component, 根据自己的业务需求定义即可)
一个Component可以依赖一个或多个Component,并拿到被依赖Component 暴露出来的实例。
tips,如果modules有多个,可以用大括号括起来:@Component(modules = {AModule.class,BModule.class})
@BaseScope
@Component(modules = UserModule.class, dependencies = BaseComponent.class)
public interface UserComponet {
void join(UserActivity userActivity);
void join(User2Activity user2Activity);
}
4.3.8 activity中测试
public class UserActivity extends AppCompatActivity {
private TextView textView;
@Inject
UserBean userBeanA;
@Inject
UserBean userBeanB;
@Inject
UserBean userBeanC;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_student);
((TextView) findViewById(R.id.text)).setText("第一个界面");
textView = findViewById(R.id.textView);
DaggerUserComponet.builder().baseComponent(((MyApplication) getApplication()).getBaseComponent()).build().join(this);
}
public void 点击全局单例(View view) {
textView.setText("userA:" + userBeanA.hashCode() + "\n"
+ "userB:" + userBeanB.hashCode() + "\n"
+ "userC:" + userBeanC.hashCode() + "\n");
}
public void 下一个全局单例(View view) {
startActivity(new Intent(this, User2Activity.class));
}
}
一开始看上去有点绕有点多,实际上可以这样理解:用了两个局部单例:不管什么activity,在使用DaggerUserComponet注入的时候,因为UserComponet依赖了baseComponent,而baseComponent是由application创建的,只会有一个。对baseComponent而言,它有的userbean又是@Singleton标注的,也是唯一。以此实现了app整个生命周期的全局单例。
补充1
Dagger2 不是通过反射的方式去实现注入的,是通过生成代码的方式来达到注入效果的,在Android的很多开源框架中都是用到了代码生成工具(APT), 比如ButterKnife。
参考文章:
Dagger2源码分析(三)从源码角度分析注解在Dagger2中的使用
轻松学,听说你还没有搞懂 Dagger2
javax.inject (Java(TM) EE 7 Specification APIs)
The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330
Dagger
@Scope 看这一篇就够了——Dagger2 (二)
dagger2从入门到放弃-Component的继承体系、局部单例
标签:Engine,依赖,入门,利器,Dagger2,单例,注解,public From: https://blog.51cto.com/u_12853553/5896547