Dagger2


https://segmentfault.com/a/1190000006665842


https://segmentfault.com/a/1190000006698289



什么是依赖注入

依赖注入就是把下面这样的代码:

class A {
    public A() {
    
}

class B {
    A a;
    
    public B() {
        a = new A();
    }
}

class C {
    A a;
    B b;
    
    public C() {
        a = new A();
        b = new B();
        b.a = a;
    }

}
main() {
    C c = new C();
}

变成:

class A {
    A() {
    }    
}

class B {
    A a;
    
    B(A a) {
        this.a = a;
    }
}

class C {
    A a;
    B b;
    
    C(A a, B b) {
       this.a = a;
       this.b = b;
    }
}
main() {
    A a = new A();
    B b = new B(a);
    C c = new C(a, b);
}

这种把对象之间的依赖生成的责任交给外界的做法,叫做依赖注入。

如何更方便地进行依赖注入

我们有类和它们之间的依赖关系,便很自然地会用图来表示这种状态。如上例子所示,可用下面这样一个图来表示:

           +-----+
           |     |
    +----> |  A  | <----+
    |      |     |      |
    |      +-----+      |
    |                   |
    |                   |
+---+---+           +---+---+
|       |           |       |
|   B   | <---------+   C   |
|       |           |       |
+-------+           +-------+

箭头表示依赖的对象。

我们想要这样的一种依赖注入框架:当我们需要一个B对象时,框架按照依赖遍历这个图,生成A,然后将其注入B,最后返回一个已经生成好的B对象。大概是:

B b = Injector.create(B.class)

另外,如果要求A对象是单例(这里不解释什么是单例)或对象的生成符合某种指定的规则,框架应自动识别并作出处理。

设计框架

我们面对两个主要问题:如何表示依赖图和如何生成对象。

依赖图的表示

我们需定义一种声明依赖的方法。可以用xml,json,甚至DSL来完成这个任务。这里我们采用比较流行和简便的注解(annotation)来表示依赖关系。

假设我们要的效果如下所示:

@dependence
class A {
}

@dependence(A.class)
class B {
}

@dependence({A.class, B.class})
class C {
}

可以看到,我们用@dependence注解来表示上面例图中的箭头。各个类之间的依赖关系十分清晰。

如果要求A是单例,我们可以这样:

@singleton
@dependence()
class A {
}

对象生成

建立了依赖图以后,需要通过某种方式生成我们需要的对象。我们希望是这样的:

B b = Injector.create(B.class)

或者通过注解实现自动注入

class Main {
    @Inject
    B b;

    main() {
        Injector.inject(this);
        }
    }

Dagger2

我们来看一下Dagger2是如何实现上述两个目标的。

依赖图的表示

Dagger2中,是通过@Inject注解或者@Module和@Provide这两个注解建立依赖图,如下所示:

首先定义好类:

public class A {
    public A(){
    }
}

public class B {
    A a;

    public B(A a) {
        this.a = a;
    }
}

public class C {
    A a;
    B b;

    public C(A a, B b) {
        this.a = a;
        this.b = b;
    }
}

然后我们用第一种方法来声明依赖:

public class A {
    @Inject
    public A() {
    }
}

public class B {
    A a;

    @Inject
    public B(A a) {
        this.a = a;
    }
}

public class C {
    A a;
    B b;

    @Inject
    public C(A a, B b) {
        this.a = a;
        this.b = b;
    }
}

可以看到我们为每一个类的方法添加了@Inject声明,表示该类是依赖图中的一个节点。如果该初始化方法含有参数,那么这些从参数也应是依赖图中的节点。

第二种方法是通过一个module类来声明依赖,如下所示:

@Module
public class ABCModule {
    @Provides
    public A provideA() {
        return new A();
    }

    @Provides
    public B provideB(A a) {
        return new B(a);
    }

    @Provides
    public C provideC(A a, B b) {
        return new C(a, b);
    }
}

@Module注解表示这个ABCModule的作用是声明“依赖图”的。@Provides注解表示当前方法的返回值是图中的一个节点,方法的参数是依赖的对象,即前文中箭头指向的目标。

再强调一次,Dagger要求图中的每一个节点都要声明,即每一个节点都要在module中有@Provides注解的方法或者@Inject注解的初始化方法。

可以看到第二种方式(module)无需修改原来的对象。为了让模块尽量少地依赖第三方库,一般采用第二种方式来声明依赖图。

对象生成

Dagger2中,从依赖图中获取对象需通过component。component是依赖图和被注入对象之间的桥梁。如下所示:

@Component(module=ABCModule.class)
public interface ABCComponent {
    public A provideA();

    public B provideB();

    public C provideC();

    void inject(Activity mainActivity);
}

@Component注解表示ABCComponent这个接口是一个Component。Component的方法隐含着如下两条规则:

  1. 不带参数的方法为“provider”方法,该方法的返回值是从依赖图中取得的对象。如下所示(伪代码):

class Main {
    
    C c;
    
    public void init() {
        c = Component.provideC();
    }
}
  1. 带参数的方法,参数为“注入对象”。通常于@Inject标签同时使用。如下所示(伪代码):

class Main {
    
    @Inject
    C c;
    
    public void init() {
        Component.inject(this);
    }
}

即调用Component.inject(foorbar)的时候,框架自动为用@Inject标签标注的属性注入依赖。要求@Inject的属性的类必须是依赖图中的节点。

注意:component的方法必需至少符合以上两条规则中的一条。

注意:provider方法的名字一般为“provider”,inject方法的名字一般为“inject”,但名字不影响这两个方法的功能。

当Component声明好以后,框架会在编译时生成一个DaggerComponent名字的类,我们可以用它来实施依赖注入,如下所示:

ABCComponent abcComponent = DaggerABCComponent.create();
A a = abcComponent.provideA();
B b = abcComponent.provideB();
C c = abcComponent.provideC();

或者:

class Main {

    @Inject
    A a;
    @Inject
    B b;
    @Inject
    C c;

    public static void main() {
        ABCComponent abcComponent = DaggerABCComponent.create();
        abcComponent.inject(this);
    }
}

Component标签的module属性可以是一个数组,即一个Component实施多个module的注入。引入类D和DModule:

class D {
    public D() {
    }    
}

@Module
public class DModule {
    @Provides
    public D provideD() {
        return new D();
    }
}

修改ABCComponent,如下:

@Component(module={ABCModule.class, DModule.class})
public interface ABCComponent {
    public A provideA();

    public B provideB();

    public C provideC();

    public D provideD();

    void inject(Activity mainActivity);
}

如上即可实现D对象的注入。

Component之间的依赖

真正实施工程的时候,会将对象以功能分类。例如network相关,DB相关,Util相关的类集中在一起管理。Dagger2为方便我们达到这一个目的,在component中引入了dependence这个功能。

例如我们有如下component

@Component(modules = DModule.class)
public interface DComponent {
    D provideD();
}

假设DComponent负责提供一个对象D。这种能力是项目无关的,我们把这个Component独立出来。然后我们可以通过@Component的dependence属性来为其他Component引入DComponent的能力。例如:

@Component(modules = ABCModule.class, dependencies = DComponent.class)
public interface ABCComponent {
    A provideA();

    B provideB();

    C provideC();

    D provideD();

    void inject(Main main);
}

可以看到,声明了dependencies=DComponent.class以后,provideD方法可以顺利拿到D对象。inject方法也可以注入D对象。

public class Main {
    @Inject
    D d; // inject D by ABCComponent

    public Main() {
        DComponent dComponent = DaggerDComponent.create();
        D d1 = dComponent.provideD(); // inject D by DComponent

        ABCComponent abcComponent = DaggerABCComponent
                .builder()
                .dComponent(dComponent)
                .build();
        D d2 = abcComponent.provideD();
        abcComponent.inject(this);
    }
}

DComponent不知道ABCComponent的存在,故可以像普通Component那样子使用。但在使用ABCComponent时,我们需要显式地为ABCComponent注入DComponent对象:

ABCComponent abcComponent = DaggerABCComponent
        .builder()
        .dComponent(dComponent)
        .build();

@Singleton

如上面例子所示,如果要求D对象为单例,可以通过@Singleton注解来实现。首先我们需要在依赖图中声明对象是单例的:

@Module
public class DModule {
    @Provides
    @Singleton
    public D provideD() {
        return new D();
    }
}

DComponent接口也需要声明:

@Singleton
@Component(modules = DModule.class)
public interface DComponent {
    D provideD();
}

如此,当我们注入D对象时,可保证每次注入的是同一个D对象:

DComponent dComponent = DaggerDComponent.create();
D d1 = dComponent.provideD(); 
D d2 = dComponent.provideD(); 

// d1 == d2

上一篇文章介绍了Dagger2的基本用法,这篇文章主要说一下Dagger2中@Scope的用法和原理。

上一篇文章中提到:

如上面例子所示,如果要求D对象为单例,可以通过@Singleton注解来实现。首先我们需要在依赖图中声明对象是单例的:

@Module
public class DModule {
    @Provides
    @Singleton
    public D provideD() {
        return new D();
    }
}

DComponent接口也需要声明:

@Singleton
@Component(modules = DModule.class)
public interface DComponent {
    D provideD();
}

如此,当我们注入D对象时,可保证每次注入的是同一个D对象:

DComponent dComponent = DaggerDComponent.create();
D d1 = dComponent.provideD(); 
D d2 = dComponent.provideD(); 
// d1 == d2

在我们看来,只是多加了一个注解而已,便实现了单例模式。要知道其原理,要从Dagger2生成的源码入手。

Dagger2生成的源码

以如下例子为例:

  1. 定义类:

    public class A {
        public A(){
        }
    }
    
    public class B {
        A a;
    
        public B(A a) {
            this.a = a;
        }
    }
    
    public class C {
        A a;
        B b;
    
        public C(A a, B b) {
            this.a = a;
            this.b = b;
        }
    }
    
  2. 定义Module

    @Module
    public class ABCModule {
        @Provides
        public A provideA() {
            return new A();
        }
    
        @Provides
        public B provideB(A a) {
            return new B(a);
        }
    
        @Provides
        public C provideC(A a, B b) {
            return new C(a, b);
        }
    }
    
  3. 定义Component接口:

    @Component(module=ABCModule.class)
    public interface ABCComponent {
        public A provideA();
    
        public B provideB();
    
        public C provideC();
    
        void inject(Main main);
    }
    
  4. 依赖注入:

    public class Main {

    @Inject
    C c;
    
    public Main() {
        ABCComponent abcComponent = DaggerABCComponent
                .builder()
                .dComponent(dComponent)
                .build();
        A a = abcComponent.provideA();
        B b = abcComponent.provideB();
        abcComponent.inject(this);
    }

    }

编译工程,Dagger2在项目路径下生成了如下文件:

[dagger2]
┣━[di]
┃  ┣━[component]
┃  ┃  ┗━DaggerABCComponent.java
┃  ┗━[module]
┃      ┣━ABCModule_ProvideAFactory.java
┃      ┣━ABCModule_ProvideBFactory.java
┃      ┗━ABCModule_ProvideCFactory.java
┗━[model]
    ┗━Main_MembersInjector.java

(利用这个工具生成了文件结构图)

注意,生成的文件在关联的类相同路径下。如DaggerABCComponent类生成在ABCComponent路径下。

我们先来看看实际接触到的DaggerABCComponent类:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerABCComponent implements ABCComponent {
  private Provider<A> provideAProvider;
  private Provider<B> provideBProvider;
  private Provider<C> provideCProvider;
  private MembersInjector<Main> mainMembersInjector;

  private DaggerABCComponent(Builder builder) {  
    assert builder != null;
    initialize(builder);
  }

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

  public static ABCComponent create() {  
    return builder().build();
  }

  private void initialize(final Builder builder) {  
    this.provideAProvider = ABCModule_ProvideAFactory.create(builder.aBCModule);
    this.provideBProvider = ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider);
    this.provideCProvider = ABCModule_ProvideCFactory.create(builder.aBCModule, provideAProvider, provideBProvider);
    this.mainMembersInjector = Main_MembersInjector.create(provideCProvider);
  }

  @Override
  public A provideA() {  
    return provideAProvider.get();
  }

  @Override
  public B provideB() {  
    return provideBProvider.get();
  }

  @Override
  public C provideC() {  
    return provideCProvider.get();
  }

  @Override
  public void inject(Main main) {  
    mainMembersInjector.injectMembers(main);
  }

  public static final class Builder {
    private ABCModule aBCModule;
  
    private Builder() {  
    }
  
    public ABCComponent build() {  
      if (aBCModule == null) {
        this.aBCModule = new ABCModule();
      }
      return new DaggerABCComponent(this);
    }
  
    public Builder aBCModule(ABCModule aBCModule) {  
      if (aBCModule == null) {
        throw new NullPointerException("aBCModule");
      }
      this.aBCModule = aBCModule;
      return this;
    }
  }
}

来看几个关键点:

  1. DaggerABCComponent继承于ABCComponent。所以我们可以直接调用ABCComponent的方法。

  2. DaggerABCComponent需要Builder来进行初始化。Builder的作用是提供对象的module。

  3. 对象通过Provider从依赖图中取出。Provider由Factory生成时会有类似依赖注入的操作。

  4. 通过MembersInjector进行依赖注入。

这几个关键类的关系可用下图表示:

+---------------------------------------+
|         DaggerABCComponent            |
|                                       |
|  +----------+  create     +-----------+------------+
|  |  Factory +-----+-----> |  Provider<A>           |
|  +----+-----+     |       +----+------+------------+
|       ^           |       |    |      |
|       |           |       | +--v------+------------+
|       |           +-----> | |Provider<B>           |
|       | ABCModule |       | +--+------+------------+
|       |           |       |    |      |
|       |           |       +----v------+------------+
|  +----+----+      +-----> |  Provider<C>           |
|  | Builder |              +-----------+------------+
|  +---------+                          |
|                                       |
| +--------------------+    +-----------+------------+
| |Main_MembersInjector+--> |  MembersInjector<Main> |
| +--------------------+    +-----------+------------+
|                                       |
+---------------------------------------+

其中最最关键的是Factory和Provider。以B类为例,从依赖图中取出B对象,需要经过如下代码:

...
this.provideBProvider = ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider);
...

@Override
public B provideB() {  
    return provideBProvider.get();
}

其中ABCModule_ProvideBFactory的源码如下所示:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ABCModule_ProvideBFactory implements Factory<B> {
  private final ABCModule module;
  private final Provider<A> aProvider;

  public ABCModule_ProvideBFactory(ABCModule module, Provider<A> aProvider) { // 根据之前的依赖关系,注入ProviderA  
    assert module != null;
    this.module = module;
    assert aProvider != null;
    this.aProvider = aProvider;
  }

  @Override
  public B get() {  
    B provided = module.provideB(aProvider.get()); // 从ProviderA中取出A对象,再生成B对象
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<B> create(ABCModule module, Provider<A> aProvider) {  
    return new ABCModule_ProvideBFactory(module, aProvider);
  }
}

Factory和Provider接口如下所示:

public interface Factory<T> extends Provider<T> {
}

public interface Provider<T> {
    T get();
}

从使用者的角度看,无需关心对象是如何生成的,只需调用provider的get方法即可获得对象。而且对象应该是符合既定的规则并且初始化好可以马上用的。

从ABCModule_ProvideBFactory(或者某个Provider)的角度看,在初始化方法里就明确了自己所需依赖的对象(这里是ProviderA)。在get方法的实现里,只需关心B对象的生成。当需要A对象时,直接从外部“注入”的providerA取出即可。

再来看一看Main_MembersInjector的实现:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class Main_MembersInjector implements MembersInjector<Main> {
  private final Provider<C> cProvider;

  public Main_MembersInjector(Provider<C> cProvider) {  
    assert cProvider != null;
    this.cProvider = cProvider;
  }

  @Override
  public void injectMembers(Main instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.c = cProvider.get();
  }

  public static MembersInjector<Main> create(Provider<C> cProvider) {  
      return new Main_MembersInjector(cProvider);
  }
}

Dagger2在编译时会分析module中inject方法的参数的类型(这里是Main类),记录下用@Inject注解标注的成员,然后生成对应的Injector。

理解Injector的关键在理解它的构造方法和injectMembers方法。instance.c = cProvider.get();一句实施了依赖注入。

@Singleton

修改ABCModule如下所示:

    @Module
    public class ABCModule {
        ...

        @Provides
        @Singleton               // 添加Singleton注解
        public B provideB(A a) {
            return new B(a);
        }

        ...
    }

修改ABCComponent如下所示:

    @Singleton
    @Component(module=ABCModule.class)
    public interface ABCComponent {
        ...
    }

我们来看看Dagger2生成的代码有什么不同:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerABCComponent implements ABCComponent {
...

private Provider provideBProvider;

...

private void initialize(final Builder builder) {

...
this.provideBProvider = ScopedProvider.create(ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider));
...

}

...

@Override public B provideB() {

return provideBProvider.get();

}


...

}

可以看到唯一的不同是用ScopedProvider将ABCModule_ProvideBFactory包裹起来。来看一下ScopedProvider的源码:

package dagger.internal;

import javax.inject.Provider;

public final class ScopedProvider<T> implements Provider<T> {
  private static final Object UNINITIALIZED = new Object();

  private final Factory<T> factory;
  private volatile Object instance = UNINITIALIZED;

  private ScopedProvider(Factory<T> factory) {
    assert factory != null;
    this.factory = factory;
  }

  @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
  @Override
  public T get() {
    // double-check idiom from EJ2: Item 71
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = factory.get();
        }
      }
    }
    return (T) result;
  }

  /** Returns a new scoped provider for the given factory. */
  public static <T> Provider<T> create(Factory<T> factory) {
    if (factory == null) {
      throw new NullPointerException();
    }
    return new ScopedProvider<T>(factory);
  }
}

理解上面的代码关键在于:

  1. ScopedProvider在dagger.internal下,非Dagger2自动生成。

  2. ScopedProvider也是一个Provider

  3. 利用double-check,在instance上实现了单例模式。也就是说,在ScopedProvider的生命周期内,get返回的都是同一个对象。

以上3点实现了无入侵式的Singleton模式。但其实ScopedProvider并不是专为Singleton模式设计的,Singleton模式只是Dagger2中Scope功能的效果。

@Scope

@Singleton注解的源码如下所示:

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

...

@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}

可以看出,@Singleton只是一个标记,表明这是一个Scope。那么Scope是什么呢?源代码中有如此注释:

A scope annotation applies to a class
containing an injectable constructor and governs how the injector reuses
instances of the type. By default, if no scope annotation is present, the
injector creates an instance (by injecting the type's constructor), uses
the instance for one injection, and then forgets it. If a scope annotation
is present, the injector may retain the instance for possible reuse in a
later injection.

简单地说,@Scope决定了注射器从依赖图中取出对象的行为。如果节点有Scope标签,那么注入时将重用上次生成的对象。

依赖图中某个节点标注了@Scope后,便拥有了与当前Component相同的生命周期。也就是说,如果要实现全局(Application范围内)的Singleton,必需要有全局的Component。这就是为什么许多其他关于Dagger2的例子中,要在Application中保持ApplicationComponent的引用的原因。

至于在许多例子中看到的@PerActivity(对象在当前Activity的生命周期内唯一),@PerUser(对象在当前用户态销毁前唯一),它们的实现也是依赖于Component的生命周期。所以需要在Activity的onCreate中新建SomeActivityComponent并保持引用,在UserManager的login中新建UserComponent并保持引用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值