Dagger

Table of Contents

1. Dagger

1.1. DI

1.1.1. 依赖

若 A 的功能依赖于 B, 则称为 A 对 B 有依赖, 没有使用 DI 时, 依赖通常有几种处理方式:

  1. 在 A 的构造函数中初始化 B, 例如:

    public A() {
        this.b = new B();
    }
    
  2. 通过 A 的 setter 来初始化 B, 例如:

    A a = new A();
    a.setB(new B());
    
1.1.1.1. 构造函数
1.1.1.1.1. 优点

通过构造函数处理依赖的优点是容易处理复杂的依赖关系: 每个依赖自身的构造函数负责处理它自己的依赖,不需要外界的干预, 例如:

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

class B {
    public B () {
        this.d = new D();
    }
}

class C {
    public C () {
        this.e = new E();
        this.f = new F();
    }
}

1.1.1.1.2. 缺点

各个依赖耦合的一起, 每个依赖不容易替换. 这种耦合对扩展和测试都不利. 例如在单元测试时, 若要测试 A, 通常需要使用 MockB, MockC 来代替 B, C.

1.1.1.2. Setter
1.1.1.2.1. 优点

容易在运行时替换单个依赖

class A {
    B b;
    C c;
}

class B {
    D d;
}

class C {
    E e;
    F f;
}

A a = new A();
B b = new B();
C c = new C();
D d = new D();
E e = new E();
F f = new F();
a.setB(b);
a.setC(c);
b.setD(d);
c.setE(e);
c.setF(f);
1.1.1.2.2. 缺点

需要人为的分析依赖的顺序并提前生成所有的依赖, 耗时且易出错.

1.1.1.3. Factory

使用构造函数不必分析依赖关系, 使用 setter 可以灵活的修改依赖, 通过 Factory 可以结合两者的优点

class A {
    B b;
    public A() {
        b = Factory.getB();
    }
}

class B {
    C c;
    public B() {
        c = Factory.getC();
    }
}

class Factory {
    B getB() {
        return new B();
    }

    C getC() {
        return new C();
    }
}

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

1.1.2. DI

Dependency Injection (DI) 的效果和 Factory 类似, 但不需要自己手写 Factory 的那些 boilerplate 代码, 它的主要功能是:

  1. 分析依赖关系
  2. 自动生成对象并使它们满足依赖
1.1.2.1. JSR-330

1.2. Dagger

dagger2 教程

Dagger 是编译时的 DI 框架: 它在编译时通过 annotation 分析依赖关系, 并生成相应的代码, 而不是在运行时通过反射及动态代码生成等方式.

Dagger 主要定义是以下的 annotation:

  1. Inject
  2. Component
  3. Module
  4. Provides
  5. Singleton

1.2.1. Inject

对于一个依赖 `A -> B`, Inject annotation 有两个作用:

class A {
    @Inject
    B b;
}

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

对于 A 来说, 把成员 b 声明为 Inject, 表示 A 依赖 B, 需要 dagger 自动给 b 赋值对于 B 来说, 把构造函数声明为 Inject, 表示 dagger 可以通过这个构造函数给需要 b 的地方 (例如 A.b) 提供一个 B 的对象.

Inject 主要用来 inject 成员变量和构造函数, 除此以外, inject 也可以:

  1. Inject 构造函数时, 构造函数的参数自动构成一个依赖

    class A {
        @Inject
        B b;
    }
    
    class B {
        @Inject
        public B(C c) {}
    }
    
    class C {
        @Inject
        public C() {}
    }
    
  2. Inject 普通成员函数, 该成员函数会在 Inject 构造函数之后自动执行, 且其参数也自动构成一个依赖

1.2.2. Component

通过 Inject 声明了依赖关系后, 需要通过 Component 来实施真正的注入动作, 因为 dagger 是编译时的 DI 框架, 期望 `new A` 时自动注入似乎并不可行.

class A {
    @Inject
    B b;
}

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

@Component
interface AInjector {
    // inject 可以是任意名字
    void inject(A a);
}

class Test() {
    public static void main(String[] args) {
        A a = new A();
        DaggerAInjector.create().inject(a);
        assert(a.b != null);
    }
}

dagger 要求 AInjector 是一个接口, 并且会自动生成一个 DaggerAInjector 的类, 通过 DaggerAInjector.create().inject(a) 可以注入 a 的依赖.

1.2.3. Module & Provides

Inject 和 Component 能实现基本的依赖分析与注入, 但有三种情况无法完成:

  1. 被依赖的是一个接口, dagger 无法对依赖进行实例化
  2. 被依赖的类的构造函数需要用户提供参数
  3. 被依赖的是第三方库, 无法给它的构造函数加个 Inject 注解

通过 Module 和 Provides 可以处理这三种情况

class A {
    @Inject
    IB b;
}

class B implements IB {

}

@Module
class TestModule {
    // provideIB 可以是任意名字, 只要返回类型一致就可以
    // 提供接口的实现
    @Provides
    IB provideIB() {
        return new B();
    }
    // 可以 provide 多个依赖
    @Provides
    C provideC() {
        return new C();
    }
    // 可以带参数
    @Provides
    D provideD() {
        return new D(1);
    }
    // provide 第三方库中的对象
    @Provides
    HttpClient provideHttpClient() {
        return new HttpClient();
    }
}

// Component 可以指定多个 module, 每个 module 对应不同的功能模块
@Component (module = {TestModule.class/*, Test2Module.class */})
interface AInjector {
    void inject(A target);
}

public class Test {
    public static void main(String[] args) {
        A a= new A();
        DaggerAInjector.create().inject(a);
        assert(a.b instanceof B);
    }
}

通过修改 Component 使用的 Module, 可以很容易的实现测试时需要的 Mock Object:

@Module
class MockBModule {
    @Provides
    IB provideIB() {
        return new MockB();
    }
}

@Component (module = MockBModule.class)
interface AUnitTestInjector {
    void inject(A target);
}

public class UnitTest {
    public static void main(String[] args) {
        A a= new A();
        DaggerAUnitTestInjector.create().inject(a);
        assert(a.b instanceof B);
    }
}

1.2.4. Scope 与单例

依赖图是唯一的, 但默认情况下 dagger 每次使用 component 注入依赖时都会按初始化新的依赖对象.

class A {
    @Inject
    B b;

    @Inject
    B b2;
}

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

@Component
interface AInjector {
    void inject(A a);
}

class Test() {
    public static void main(String[] args) {
        A a = new A();
        DaggerAInjector.create().inject(a);
        assert(a.b != a.b2);
    }
}

有两种方式可以实现单例:

  1. 通过自己控制 module 的 provideXXX 返回一个单例的对象
  2. 通过 Scope
1.2.4.1. Module 实现单例
class A {
    @Inject
    B b1;

    @Inject
    B b2;
}

class B {}

@Module
class TestModule {
    static B sB = new B();
    @Provides
    B provideB() {
        return sB;
    }
}

@Component (module = TestModule.class)
interface AInjector {
    void inject(A target);
}

public class Test {
    public static void main(String[] args) {
        A a= new A();
        DaggerAInjector.create().inject(a);
        assert(a.b1 == a.b2)
    }
}

1.2.4.2. Scope 实现单例
  1. 声明一个 Scope annotation
  2. 声明 Component 属于这个 scope
  3. 声明 Provides A 属于这个 scope

则使用同一个 Component 注入的 A 都是重用的

class A {
    @Inject
    B b1;

    @Inject
    B b2;
}

@Scope
@interface TestScope {}

@Module
class TestModule {
    @Provides
    @TestScope
    B provideB() {
        return new B();
    }
}

@TestScope
@Component (modules = TestModule.class)
interface AInjector {
    void inject(A target);
}

public class Test {
    public static void main(String argv[]) {
        A a = new A();
        AInjector injector = DaggerAInjector.create();
        injector.inject(a);
        assert(a.b1 == a.b2);
    }
}

1.3. Dagger Internal

1.3.1. 简单的 inject

// --------------------------------
class BBB {
    @Inject
    public BBB() {}
}

@Component
interface BasicComponent {
    void inject(AAA target);
}

class AAA {
    @Inject
    BBB bbb;
}

// ---------------------------------
public class HelloDaggerSimple {
    static void testInjection() {
        AAA program = new AAA();
        DaggerBasicComponent.create().inject(program);

    }

    public static void main(String argv[]) {
        testInjection();
    }
}

// 自动生成的代码:

DaggerBasicComponent:

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

public void inject(AAA target) {
    injectAAA(target);
}

private AAA injectAAA(AAA instance) {
    AAA_MembersInjector.injectBbb(instance, new BBB());
    return instance;
}

// AAAMembersInjector:
public static void injectBbb(Object instance, Object bbb) {
    ((AAA) instance).bbb = (BBB) bbb;
}

// 所以 DaggerBasicComponent.create().inject(program) 展开后等价为:
program.bbb = new BBB();

1.3.2. 使用 module

class BBB {
    public BBB() {}

}

@Module
class TestModule {
    @Provides
    BBB provideBBB() {
        return new BBB();
    }
}

@Component(modules = TestModule.class)
interface AAAComponent {
    void inject(AAA aaa);

}

class AAA {
    @Inject
    BBB mClient;
}

public class HelloDaggerModule {
    static void testModule () {
        AAA aaa = new AAA();
        DaggerAAAComponent.create().inject(aaa);
    }

    public static void main(String argv[]) {
        testModule();
    }
}

// DaggerAAAComponent.create()
this.testModule = new TestModule();
return new DaggerAAAComponent(this);

// inject
AAA_MembersInjector.injectMClient(
    instance, TestModule_ProvideBBBFactory.proxyProvideBBB(testModule));

// TestModule_ProvideBBBFactory.proxyProvideBBB
return instance.provideBBB()

// AAAMembersInjector.injectMClient
((AAA) instance).mClient = (BBB) mClient;

// 所以  DaggerAAAComponent.create().inject(aaa) 展开为:
aaa.mClient = (new TestModule()).provideBBB();

// TestModule 的作用与 Factory 类似

Author: [email protected]
Date: 2019-07-03 Wed 00:00
Last updated: 2024-02-01 Thu 14:04

知识共享许可协议