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 } }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构造出来。
默认情况下,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结尾。
使用@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(); } }