Dependency Injection
Dependency指的是一个可以用的对象,也叫服务。Injection指的是把依赖传递给需要使用它的dependent对象,也叫客户端。服务是客户端状态的一部分。
依赖注入模式的基本要求是把服务传递给客户端,而不是让客户端自己去创建或者寻找服务。
这个基本要求也就意味着在不允许类内部使用new或者static方法去产生服务。该类应该从外部接收服务,因此服务的获取是其他类的职责。
依赖注入原则的目的是创建和使用的解耦,达到一种境界,就是当客户端所依赖的对象需要变成另一个的时候,客户端的代码也无需修改。
Dagger2是用来实现Dependency Injection模式的库。依赖注入一方面可以方便测试,另一方面可以创建可复用可更换的模块。
Dagger2的具体实现是在编译期生成代码。
导入
Java Gradle
// Add plugin https://plugins.gradle.org/plugin/net.ltgt.apt
plugins {
id "net.ltgt.apt" version "0.10"
}
// Add Dagger dependencies
dependencies {
compile 'com.google.dagger:dagger:2.x'
apt 'com.google.dagger:dagger-compiler:2.x'
}
Android Gradle
dependencies {
compile 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
// 如果要使用dagger.android还需要导入
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // 如果使用支持库
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
}
如果使用了DataBinding,可以通过以下方式增加javac所会打印的错误行数:
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xmaxerrs" << "500" // or whatever number you want
}
}
使用步骤
1. 声明依赖
Dagger可以创建所需要的类实例,满足所需要的依赖。它使用javax.inject.Inject来识别目标构造方法和需要的字段。
使用@Inject来标注用来创建类实例的构造方法,需要创建类实例的时候,Dagger会去获取需要的参数值,然后调用这个构造方法。
class Thermosiphon implements Pump {
private final Heater heater;
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
...
}
Dagger可以直接注入字段,比如下面这段代码就是给heater这个字段获取一个Heater实例,给pump这个字段获取一个Pump实例。
class CoffeeMaker {
@Inject Heater heater;
@Inject Pump pump;
...
}
如果类中有@Inject注解的字段,但是没有@Inject注解的构造方法,Dagger会在需要的时候注入@Inject注解字段,但是不会创建新的实例。新添一个@Inject注解的无参构造函数来表示说Dagger也可以创建新实例。
Dagger也支持方法注入,尽管比较少用。
缺少@Inject注解的类不能被Dagger构造出来。
2. 满足依赖
默认情况下,Dagger通过构建所需要类型的实例来满足依赖,如果你要求一个CoffeeMaker实例,Dagger就会调用new CoffeeMaker(),然后设置可注入字段。
@Inject在以下情况下不管用:
- 接口没办法被实例化
- 第三方类没办法被注解
- 可配置的类必须被配置
在这些@Inject无能为力的情况下,可以使用@Provides注解方法来满足依赖条件,所注解的方法返回需要的依赖类。
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
比如说需要一个Heater的时候就会去调用provideHeater()。
@Provides所注解的方法也可能有自己的依赖,比如说
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
所有的@Provides注解方法必须声明在一个注解了@Module的类当中
@Module
class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}
根据习惯,@Provides注解的方法都用provide开头,@Module注解的类都用Module结尾。
3. 构建图表
使用@Inject和@Provides注解的类相互之间通过依赖关系联系在了一起。Dagger2通过一个注解了@Component的接口来获取需要的类。该接口中的方法都没有参数,返回的是所需要的类型。@Component注解需要指明返回的module类名,如有多个使用逗号分开:
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
这样会生成一个Dagger+Component名的类用来获取想要的对象:
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
注意,如果@Component注解的类是内部类的话,还需要加上外部类的类名,以下划线连接,例如:
class Foo {
static class Bar {
@Component
interface BazComponent {}
}
}
那生成类的类名就是DaggerFoo_Bar_BazComponent。
Module类自带的可获取的构造方法都可以直接省略掉,因为如果没有设置的话,builder可以直接创建一个实例。
如果Module类的@Provides方法都是static的,那就不需要builder去创建实例了,直接使用create()即可:
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
使用示例:
public class CoffeeApp {
public static void main(String[] args) {
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}