Dagger 2.15解析(实例+源码)


Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier versioncreated by Square and now maintained by Google.
Dagger aims to address many of the development and performance issues that have plagued reflection-based solutions.

Dagger是一个完全静态的、适用于Java和Android的编译时依赖注入框架,早期由Square维护,现在由Google维护。Dagger致力于解决反射引入的开发和性能上的问题。

Dagger是基于APT的。

APT(Annotation Processing Tool)注解处理工具,对源码文件进行检测,找出其中的Annotation,进行额外的处理。

Dagger2 github地址:


依赖注入过程需要三部分:依赖提供方,依赖注入容器,依赖需求方

比如说我们在TestActivity中需要拿到Teacher对象的引用,一般来说是new一个对象。

在Dagger2中,TestActivity是依赖需求方,Component是依赖注入容器,@Inject或者@Module & @Provides作为依赖提供方。

控制反转(Invercation of Control)Ioc: Ioc控制反转是一种设计思想,比如说ClassA中引用到了ClassB。Ioc 是将你设计好的对象classB交给容器控制,而不是传统的在你的对象classA内部直接控制classB。

如果是在classA中通过new ClassB()主动创建,得到依赖对象classB的引用,正转,高耦合;
Ioc容器去创建、查找并注入依赖对象classB, classA只是被动的接受依赖对象classB,反转 ,松耦合;

依赖注入 (Depedency Injection)DI: DI依赖注入是指由容器动态的将某个依赖关系注入到组件之中,组件复用,classA依赖于Ioc容器来提供对象需要的外部资源classB 。

Java反射机制: 是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

接下来介绍基本使用。在module的dependencies下面引入dagger2

implementation "com.google.dagger:dagger:2.15"
annotationProcessor "com.google.dagger:dagger-compiler:2.15"

1、@Inject 修饰构造函数

  • 构造方法通过@Inject修饰,依赖提供方
public class Teacher {
    @Inject
    public Teacher() {}
}
  • component 连接依赖提供方和依赖需求方
@Component()
public interface TestComponent {
    void inject(TestActivity testActivity); //这个名字可修改为xxx
}
  • 依赖需求方,通过@Inject得到一个Teacher的实例,同时需要DaggerTestComponent来连接依赖提供方和依赖需求方。
  • DaggerTestComponent是编译后,系统自动生成的,名为Dagger + 刚声明的component。
public class TestActivity extends AppCompatActivity {
    @Inject
    Teacher teacher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dagger_test);
  
    //DaggerTestComponent.create().inject(this); create()等同于builder().build()
       DaggerTestComponent.builder()
               .build()
               .inject(this); //同component中注入的方法名xxx
        Log.e("zhen", teacher.toString()); // Teacher@63e4d73
    }
}

我们就建立了1个Teacher类,一个TestComponent接口,一个activity,实现了功能

在generated/source/apt/debug/包名/下,系统会生成四个文件,接下来对这些文件进行一下分析。Student_Factory是我用来测试用的,只是复制一个Teacher,修改类名为Student,不影响功能。

DaggerTestComponent.java

DaggerTestComponent继承自TestComponent,

DaggerTestComponent.builder().build(),其实就是通过构造者模式得到一个DaggerTestComponent的实例。

inject(TestActivity),是注入需求方想要的依赖。

public final class DaggerTestComponent implements TestComponent {
  private DaggerTestComponent(Builder builder) {}

  public static Builder builder() {
    return new Builder();
  }

  public static TestComponent create() {
    return new Builder().build();
  }

  @Override
  public void inject(TestActivity testActivity) { //同component中的注入方法名xxx,不信可以修改试一下
    injectTestActivity(testActivity); //因为DaggerTestComponent继承自TestComponent 
  }

  private TestActivity injectTestActivity(TestActivity instance) {
    TestActivity_MembersInjector.injectTeacher(instance, new Teacher());
    //假如我们又声明了一个student,并通过@Inject标注构造方法,然后TestActivity通过@Inject引入了student
    //TestActivity_MembersInjector.injectStudent(instance, new Student()); //则会多出来这么一句。
    return instance;
  }

  public static final class Builder { //静态内部类得到一个外部类的实例
    private Builder() {}

    public TestComponent build() {
      return new DaggerTestComponent(this);
    }
  }
}

TestActivity_MembersInjector.java

这里是@Inject注入了Teacher和Student的情况,可以幻想一下没有Student的情况。

  • injectTeacher(TestActivity instance, Teacher teacher)

      instance是建立注入关系时,传入的this实例
      teacher是上面传过来的new Teacher()
    
public final class TestActivity_MembersInjector implements MembersInjector<TestActivity> {
  private final Provider<Teacher> teacherProvider;
  //private final Provider<Student> studentProvider; 假如声明了student的话

  public TestActivity_MembersInjector(Provider<Teacher> teacherProvider) {
    this.teacherProvider = teacherProvider;
  }

  public static MembersInjector<TestActivity> create(Provider<Teacher> teacherProvider) {
    return new TestActivity_MembersInjector(teacherProvider);
  }

  @Override
  public void injectMembers(TestActivity instance) {
    injectTeacher(instance, teacherProvider.get());
  }

  public static void injectTeacher(TestActivity instance, Teacher teacher) {
    instance.teacher = teacher;
  }
}

TeacherFactory.java
这个就不介绍了,看上一张图。

2、Module 提供依赖

  • Teacher的构造方法去掉@Inject,普通的bean类
public class Teacher {
    public Teacher() { }
}
  • Module类通过@Module标注
  • @Provides标记提供依赖的方法。一般来说写成provideXXX,这里我写成provideTeacherHAHA,做实验。
@Module
public class TestModule {

    @Provides
    Teacher provideTeacherHAHA() {
        return new Teacher();
    }
}
  • component接口还是通过@Component标注
  • 里面会多出来个modules = TestModule.class (module自定义名字哈,前面我们也说过了inject方法名可以自定义哈)
@Component(modules = TestModule.class)
public interface TestComponent {

    void inject(TestActivity testActivity);
}
  • 使用上这里好像和 @Inject标记构造方法 没什么两样
public class TestActivity extends AppCompatActivity {

    @Inject
    Teacher teacher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dagger_test);
        
       DaggerTestComponent.builder()
//             .testModule(new TestModule()) //这个可去掉,因为build()中,如果TestModule为空,会主动new一个,
               .build() //同理,builder().build() 可以用 create()替代
               .inject(this);

        Log.e("zhen", teacher.toString());
    }
}

但是,当我们修改这些后,再次编译,DaggerTestComponent里可大有乾坤

TestActivity_MembersInjector没有变化,但是Teacher_Factory没了,换成了TestModule_ProvideTeacherHAHAFactory,DaggerTestComponent里有些许变化。

DaggerTestComponent.java

最大的变化就是里面多了个成员变量TestModule,所以构造函数发生了变化;构造器里也发生了变化。

inject() 同样的对用户隐藏了实现细节,只是由上面的new Teacher() 换成了TestModule_ProvideTeacherHAHAFactory.proxyProvideTeacherHAHA(testModule)。

public final class DaggerTestComponent implements TestComponent {
  private TestModule testModule; // 1、DaggerTestComponent多了个成员变量TestModule

  private DaggerTestComponent(Builder builder) { 
    initialize(builder);  //2.1、构造函数变化了,需要给testModule赋值
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.testModule = builder.testModule; //2.2、构造函数变化了,需要给testModule赋值,可能为空
  }
  
  public static Builder builder() {
    return new Builder(); 
  }

  public static TestComponent create() {
    return new Builder().build(); //返回当前实例
  }

  @Override
  public void inject(TestActivity testActivity) {
    injectTestActivity(testActivity);
  }

  private TestActivity injectTestActivity(TestActivity instance) { 
    TestActivity_MembersInjector.injectTeacher(
        instance, TestModule_ProvideTeacherHAHAFactory.proxyProvideTeacherHAHA(testModule));
    return instance;
  }

  public static final class Builder {
    private TestModule testModule; //3、构造器中也多了一个TestModule

    private Builder() {}

    public TestComponent build() {
      if (testModule == null) { //3.2、如果testModule为空,则new一个
        this.testModule = new TestModule();
      }
      return new DaggerTestComponent(this);
    }

    public Builder testModule(TestModule testModule) { //3.1、指定TestModule
      this.testModule = Preconditions.checkNotNull(testModule); 
      return this;
    }
  }
}

TestModule_ProvideTeacherHAHAFactory.java

这个文件的命名规则是A_BFacory,A是定义的module类名,B是里面通过@Provides标记的方法名。

proxyProvideTeacherHAHA(testModule) => testModule.provideTeacherHAHA(),是不是有种真相大白的感觉,这里是由Factory来回调module中提供的provides方法,最终得到依赖的实例。

public final class TestModule_ProvideTeacherHAHAFactory implements Factory<Teacher> {
  private final TestModule module;

  public TestModule_ProvideTeacherHAHAFactory(TestModule module) {
    this.module = module;
  }

  @Override
  public Teacher get() {
    return Preconditions.checkNotNull(
        module.provideTeacherHAHA(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static TestModule_ProvideTeacherHAHAFactory create(TestModule module) {
    return new TestModule_ProvideTeacherHAHAFactory(module);
  }

  public static Teacher proxyProvideTeacherHAHA(TestModule instance) {
    return Preconditions.checkNotNull(
        instance.provideTeacherHAHA(), "Cannot return null from a non-@Nullable @Provides method");
  }
}

目前看来,@Inject 修饰构造函数和Module 提供依赖的方式,都可以简单的实现依赖注入。对于我们来说并没有什么不一样?其实并不是的,通过构造方法实现注入有很大的局限性。

  • 1、只能标记一个构造方法,如果我们需要标记多个构造方法,编译的时候就会报错。
  • 2、不能标记其它我们自己不能修改的类,如第三方库,因为我们没办法用@Inject标记它们的构造函数。

针对局限性1,我们来实践一下。

3、@Named 注入多种

  • 首先我们给Teacher提供两个构造方法,一个无参,一个有参
public class Teacher {
    String name;

    public Teacher() { } //老师实例化没有名字

    public Teacher(String name) { //老师实例化,指定名字
       this.name = name;
    }
}
  • 同理,provide两个不同的实例,
  • 注,要通过@Named标记,不然会报错,一个module中不能提供两个方法返回相同的对象类型。
  • 带有参数的依赖对象,参数也是要通过@inject或者@module & @provides提供的,这里直接简化了
@Module
public class TestModule {

    @Provides
    Teacher provideTeacherHAHA() {
        return new Teacher();
    }

    @Provides
    @Named("xxx") //注意啦
    Teacher provideTeacherWithName() {
        return new Teacher("王尼玛");
    }
}

  • TestActivity.java中需要使用带参的teacher,也要通过@Named()标注,
    @Inject
    @Named("xxx")
    Teacher teacher1;

就这么简单,同样的我们将要探寻内部发生了什么。

@Named

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}

DaggerTestComponent.java

  • 这里还是通过HAHAFactory来得到一个无参的Teacher
  • 这里通WithNameFactory来得到一个有参的Teacher,也就是说通过@Provides标注的方法都会有对应的A_BFactory
  • TestActivity_MembersInjector 里有injectTeacher(),还有injectTeacher1(),也就说每次在TestActivity通过依赖注入一个对象,TestActivity_MembersInjector就会增加相应的方法。
private TestActivity injectTestActivity(TestActivity instance) {
	//这里还是通过HAHAFactory来得到一个无参的Teacher
    TestActivity_MembersInjector.injectTeacher( 
        instance, TestModule_ProvideTeacherHAHAFactory.proxyProvideTeacherHAHA(testModule));
   //这里通WithNameFactory来得到一个有参的Teacher
    TestActivity_MembersInjector.injectTeacher1( 
        instance, TestModule_ProvideTeacherWithNameFactory.proxyProvideTeacherWithName(testModule));
    return instance;
  }

TestModule_ProvideTeacherWithNameFactory.java

和TestModule_ProvideTeacherHAHAFactory几乎是一个套路呀,,,,

public final class TestModule_ProvideTeacherWithNameFactory implements Factory<Teacher> {
  private final TestModule module;

  public TestModule_ProvideTeacherWithNameFactory(TestModule module) {
    this.module = module;
  }

  @Override
  public Teacher get() {
    return Preconditions.checkNotNull(
        module.provideTeacherWithName(),
        "Cannot return null from a non-@Nullable @Provides method");
  }

  public static TestModule_ProvideTeacherWithNameFactory create(TestModule module) {
    return new TestModule_ProvideTeacherWithNameFactory(module);
  }

  public static Teacher proxyProvideTeacherWithName(TestModule instance) {
    return Preconditions.checkNotNull(
        instance.provideTeacherWithName(),
        "Cannot return null from a non-@Nullable @Provides method");
  }
}

TestActivity_MembersInjector.java

相当于对teacher做了一次拷贝,逻辑一致。

public final class TestActivity_MembersInjector implements MembersInjector<TestActivity> {
  private final Provider<Teacher> teacherProvider;
  private final Provider<Teacher> teacher1Provider;

  @Override
  public void injectMembers(TestActivity instance) {
    injectTeacher(instance, teacherProvider.get());
    injectTeacher1(instance, teacher1Provider.get());
  }

  public static void injectTeacher(TestActivity instance, Teacher teacher) {
    instance.teacher = teacher;
  }

  public static void injectTeacher1(TestActivity instance, Teacher teacher1) {
    instance.teacher1 = teacher1;
  }
}

4、@Qualifier

作用,我感觉就是替换@Named,@Named的里面的字符串需要一致,这个注解标签写起来不容易出错一些。

自定义一个名字,作为一个标签。然后用这个标签去替代一下@Named()就行

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface TeacherWithName {
}

替换module中的@Named

    @Provides
    @TeacherWithName
    Teacher provideTeacherWithName() {
        return new Teacher("王尼玛");
    }

替换TestActivity中的@Named

    @Inject
    @TeacherWithName
    Teacher teacher1;

5、@Singleton

@Singleton并不是真的能创建单例,但我们依然可以保证在App的生命周期内一个类只存在一个对象。@Singleton更重要的作用是通过标记提醒我们自己来达到更好的管理实例的目的。

  • 1、在module中的provideXX上,通过@Singleton标记对象
@Module
public class TestModule {

    @Singleton //通过@Singleton标记对象
    @Provides
    Teacher provideTeacherHAHA() {
        return new Teacher();
    }
}
  • 2、在Component接口也通过@Singleton标记
@Singleton
@Component(modules = TestModule.class)
public interface TestComponent {

    void inject(TestActivity testActivity);

    void inject(Test2Activity test2Activity);
}
  • 3、分别在TestActivity和Test2Activity中注入依赖对象
TestComponent component = DaggerTestComponent.builder()
               .testModule(new TestModule())
               .build();
component.inject(this);
Log.e("zhen", "teacher: " + teacher.toString() + "  component: " + component.toString());

我们会惊喜的发现TestActivity和Test2Activity中的teacher不是一个对象, component也不是一个对象。 正是因为component不是一个对象,所以Teacher也不是一个对象。我们试试在BaseApplication中提供一个全局的component试试。

BaseApplication中通过static提供一个唯一实例component中,然后我们在TestActivity和Test2Activity中,使用这个component来注入依赖关系,嗯嗯,teacher是同一个对象,component也是同一个对象。

public class BaseApplication extends Application {
    static TestComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerTestComponent.builder()
                .testModule(new TestModule())
                .build();
    }

    public static TestComponent getComponent() {
        return component;
    }
}

6、@Scope

我们可以看出@Singleton其实就是@Scope的一个默认实现而已,除了@Singleton,重点是同一个component。此外,我们任意自定义的@Scope都可以替代@Singleton,可以实现同样的效果。但是@Singleton更是一种标记,一般人不会那样做。

@Scope注解改变的只是做一个标记,然后Component将有标记的对象工厂类进行了一次”DoubleCheck“单例加工!

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

@Singleton是基于全局,Application的单例。一般来说,我们用自定义@Scope来实现局部单例。

比如,我在TestActivity中需要实例化两个Teacher对象,在Test2Activity中也需要要实例化两个Teacher对象,但我希望TestActivity中的两个Teacher都叫小明,但Test2Activity中的两个Teacher都叫小红。

1、我们先定义一个ActivityScope

@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

2、在module里的@provides上,和@Component上面,都标注@ActivityScope

3、分别在TestActivity和Test2Activity中注入依赖对象

TestComponent component = DaggerTestComponent.builder()
               .testModule(new TestModule())
               .build();
component.inject(this);
Log.e("zhen", "teacher: " + teacher.toString() + "  component: " + component.toString());
Log.e("zhen", "teacher1: " + teacher1.toString() + "  component: " + component.toString());

我们会发现在TestActivity注入的是同一个对象A,在Test2Activity注入的是同一个对象B,但A不同于B,也就是说实现了不同Activiyty之间不共享实例,但是同一个Activity内是共享一个实例的。 实现这个效果的重点是不同的component,标注了同一种自定义@Scope。

此外为了探究之间的关系,我还做了种种实验。

  • 1、只要没标注Scope,不管使没使用同一个component,在TestActivity和Test2Activity中的对象每一个都不一样

  • 2、标注了Scope,且使用同一个component,在TestActivity和Test2Activity中的对象都表示同一个对象

  • 3、标注了Scope,在TestActivity和Test2Activity中使用不同的component,在TestActivity注入的是同一个对象A,在Test2Activity注入的是同一个对象B,但A不同于B

  • 4、有dependencies的,不需要管dependencies,结论同上。

      Component和它的Module要使用相同的Scope。同一个Module中需要使用同样的Scope。
      有依赖关系或者包含关系的Component不能使用同样的Scope,相同的会报错
      component是实例化是是DaggerxxComponent.builder().build()
    

综上所述,指定了Scope,会根据Component的对象个数,得到依赖对象Teacher的对象个数

7、dependencies

假设我们需要一个Person对象,由ActivityComponent来提供;person里的参数Context,由ActivityComponent来提供。也就是说ActivityComponent依赖于AppComponent。

ActivityModule 依赖提供方

前面我们说过带有参数的依赖对象,参数也是要通过@inject或者@module & @provides提供的,也就是AppComponent中提供的哈。

@Module
public class ActivityModule {

    @Provides
    public Person providePerson(Context context){
        return new Person(context);
    }
 }

ActivityComponent 依赖注入容器

@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {

    void inject(TestActivity testActivity);
}

AppModule中的context是通过AppModule实例化传过来的

@Module
public class AppModule {
    private Context context;

    public AppModule(Context context) {
        this.context = context;
    }

    @Provides
    public Context providesContext() {
        return context;
    }
}

AppComponent

这个AppComponent接口内没有inject方法,因为具体地注入哪个类,是由依赖它的Component决定的。

@Component(modules = AppModule.class)
public interface AppComponent {
    Context getContextHAHA();
}

在我们的Activity中

当然appComponent可以放在刚才我们说的BaseApplication中去,那么那里的this指的就是appliaction啦,而这里的this指的是TestActivity的上下文。这里只是为了演示。

AppComponent appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this)) //必须,不然会报错
                .build();
//必须,且里面用到了appComponent.getContextHAHA() 得到context对象
        ActivityComponent activityComponent = DaggerActivityComponent.builder()
                .appComponent(appComponent) 
                .activityModule(new ActivityModule())
                .build();
        activityComponent.inject(this);

具体的细节可以看看。appComponent为空会报错,appModule为空也会报错,也就是前面说的两个必须。

8、懒加载Lazy和强制重新加载Provider

在注入时分别使用 Lazy 和 Provider 修饰要注入的对象:

说明 lazyPerson 多次get 的是同一个对象,providerPerson多次get,每次get都会尝试创建新的对象。

     @Inject
    Lazy<Person> person0;

    @Inject
    Provider<Person> person1;
    
    Log.e("zhen", person0.get().toString()); // Person@9c4dfc4
    Log.e("zhen", person0.get().toString()); // Person@9c4dfc4
    Log.e("zhen", person1.get().toString());// Person@a992ead
    Log.e("zhen", person1.get().toString());// Person@63fc5e2
9、Dagger是如何查找所需的依赖实例并进行注入的呢?

(前提是@Conponent标记的接口中包含了@Module标记的Module类,如果没有则直接找@Inject对应的构造方法,如果都没有找到,会报错。)

  • 步骤1:查找Module中是否存在创建该类的方法

  • 步骤2:若存在创建类方法,查看该方法是否存在参数

      步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
      步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
    
  • 步骤3:若不存在创建类方法,则查找@Inject注解的构造函数,看构造函数是否存在参数

      步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
      步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
    

10、总结

  • @Inject

Inject主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖,另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖。

  • @Provide

用Provide来标注一个方法,该方法可以在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Injection的变量赋值。provide主要用于标注Module里的方法

  • @Module

用Module标注的类是专门用来提供依赖的。有的人可能有些疑惑,看了上面的@Inject,需要在构造函数上标记才能提供依赖,那么如果我们需要提供的类构造函数无法修改怎么办,比如一些jar包里的类,我们无法修改源码。这时候就需要使用Module了。Module可以给不能修改源码的类提供依赖,当然,能用Inject标注的通过Module也可以提供依赖

  • @Component

Component一般用来标注接口,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值