Dagger
Table of Contents
1. Dagger
1.1. DI
1.1.1. 依赖
若 A 的功能依赖于 B, 则称为 A 对 B 有依赖, 没有使用 DI 时, 依赖通常有几种处理方式:
在 A 的构造函数中初始化 B, 例如:
public A() { this.b = new B(); }
通过 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.1.2.1. JSR-330
1.2. Dagger
Dagger 是编译时的 DI 框架: 它在编译时通过 annotation 分析依赖关系, 并生成相应的代码, 而不是在运行时通过反射及动态代码生成等方式.
Dagger 主要定义是以下的 annotation:
- Inject
- Component
- Module
- Provides
- 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 也可以:
Inject 构造函数时, 构造函数的参数自动构成一个依赖
class A { @Inject B b; } class B { @Inject public B(C c) {} } class C { @Inject public C() {} }
- 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 能实现基本的依赖分析与注入, 但有三种情况无法完成:
- 被依赖的是一个接口, dagger 无法对依赖进行实例化
- 被依赖的类的构造函数需要用户提供参数
- 被依赖的是第三方库, 无法给它的构造函数加个 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); } }
有两种方式可以实现单例:
- 通过自己控制 module 的 provideXXX 返回一个单例的对象
- 通过 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 实现单例
- 声明一个 Scope annotation
- 声明 Component 属于这个 scope
- 声明 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 类似