文章目录
-
- 1.概述
- 2.DECLARING DEPENDENCIES(声明依赖)
- 3.满足依赖关系(SATISFYING DEPENDENCIES)
- 4.建图(BUILDING THE GRAPH)
- 5.单例(SINGLETONS)
- 6.懒加载(LAZY INJECTIONS)
- 7.PROVIDER INJECTIONS
- 8.限定符(QUALIFIERS)
- 9.静态注入(STATIC INJECTION)
- 10.编译时验证 (COMPILE-TIME VALIDATION)
- 11.编译时代码生成(COMPILE-TIME CODE GENERATION)
- 12.模块重写(MODULE OVERRIDES)
dagger官方文档
dagger1 git
1.概述
依赖注入(Dependency Injection)具体含义是:
当某个角色(一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在依赖注入器中,创建被调用者的工作不再由调用者来完成(控制反转)
Dagger作为工厂类的替代品,声明依赖、指定如何满足依赖、主导应用程序。基于JSR-330,方便每个类测试。依赖注入不仅仅用于测试,还便于创建可重用、可替换的模块。可在所有应用程序中共享相同的AuthenticationModule(验证模块),可在开发期间运行DevLoggingModule(开发者登录模块),并在生产环境中运行ProdLoggingModule(用户登录模块),以在每种场景下获得正确的行为
2.DECLARING DEPENDENCIES(声明依赖)
使用 javax.inject.Inject 注解来识别构造函数和属性:
(1)使用@Inject注解类的构造函数,当依赖注入构建实例时,Dagger会获得所需的参数值并调用此构造函数
import javax.inject.Inject;
class Thermosiphon implements Pump {
private final Heater heater;
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
@Override public void pump() {
if (heater.isHot()) {
System.out.println("=> => pumping => =>");
}
}
}
(2)Dagger直接注入属性:
import dagger.Lazy;
import javax.inject.Inject;
class CoffeeMaker {
@Inject Lazy<Heater> heater;
@Inject Pump pump;
public void brew() {
heater.get().on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
heater.get().off();
}
}
若类中有@Inject注解在属性上,但没有构造函数使用@Inject注解,Dagger会使用无参数构造函数(存在无参构造函数)。
Dagger不支持方法注入。
3.满足依赖关系(SATISFYING DEPENDENCIES)
默认情况下,Dagger通过构造符合类型的实例来满足每个依赖。
当请求注入一个CoffeeMaker时,通过调用new CoffeeMaker()并设置可注入的属性。
@Inject并不适用于所有地方:
接口不能被构造;
第三方类不能被注解;
必须配置可配置对象。
对于不适用@Inject的情况,使用@ Provides注解方法来满足依赖。方法的返回类型定义了它所满足的依赖项。
在任何需要Heater的时候,调用provideHeater()方法
@Provides注解的方法可能有自己的依赖。当需要Pump 时,返回Thermosiphon :
所有@Provides注解的方法必须在一个模块中,有@Module标准的类
按照惯例,@Provides注解的方法方法名使用前缀provide,而模块类的类名使用后缀Module。
4.建图(BUILDING THE GRAPH)
@Inject和@ Provides注释的类形成了一张通过依赖连接的对象图。通过调用ObjectGraph.create()获得此图,该函数接受一个或多个模块作为参数:
为了使用图,需要引导注入。这通常需要注入命令行app的主类。在示例中,使用CoffeeApp类用于启动依赖注入。要求图提供一个类的注入实例:
import javax.inject.Inject;
import dagger.ObjectGraph;
public class CoffeeApp implements Runnable {
@Inject CoffeeMaker coffeeMaker;
@Override public void run() {
coffeeMaker.brew();
}
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());
CoffeeApp coffeeApp = objectGraph.get(CoffeeApp.class);
coffeeApp.run();
}
}
唯一缺少的是可注入类CoffeeApp不为图所知,需要显式地在@Module注解中将它注册为可注入类型。
这些injects允许在编译时验证整张图。尽早发现问题加快开发速度,并减少重构带来的危险。
现在已经构建了图并注入了根对象,运行coffee maker应用程序。
5.单例(SINGLETONS)
用@Singleton注解 带@ Provides的方法或可注入类。对象图将为所有使用者使用单例。
注入类上的@Singleton注释可作为说明文档使用,提醒维护者人员,这可能由多线程共享。
6.懒加载(LAZY INJECTIONS)
有时需要在使用时实例化对象。对于任何绑定类型T,可创建 Lazy,延迟实例化直到第一次调用Lazy的get()方法。如果T单例,那么Lazy为ObjectGraph范围内所有注解注入的同一实例。否则,每个注入点将获得其特有的Lazy实例。
class GridingCoffeeMaker {
@Inject Lazy<Grinder> lazyGrinder;
public void brew() {
while (needsGrinding()) {
// Grinder created once on first call to .get() and cached.
lazyGrinder.get().grind();
}
}
}
7.PROVIDER INJECTIONS
有时需要返回多个实例,而不是只注入一个值。存在多种方法(工厂模式、构造模式),其中一个方法是注入一个Provider。Provider每次调用.get()方法时,创建一个新的T类型实例。
注意:注入Provider可能产生混乱的代码,在对象图中产生作用域错误或结构错误的对象。通常,使用Factory或Lazy或者重新组织代码的生命周期和结构,以便注入T。但是,在某些情况下使用Provider.如,servlet在设计上是单例的,仅在请求特定数据的上下文中有效。
8.限定符(QUALIFIERS)
有时类型不足以识别依赖,需要添加了一个限定符注解。任何使用@Qualifier的注解均属于限定符注解。例如@Named(javax.inject的内置限定符注解)
可创建自己的限定符注释,或者使用@Named注解。通过注解所需的属性或参数来运用限定符。类型和限定符注释都将用于识别依赖。
class ExpensiveCoffeeMaker {
@Inject @Named("water") Heater waterHeater;
@Inject @Named("hot plate") Heater hotPlateHeater;
}
通过搭配@ Provide注解方法来提供限定值。
@Provides @Named("hot plate") Heater provideHotPlateHeater() {
return new ElectricHeater(70);
}
@Provides @Named("water") Heater provideWaterHeater() {
return new ElectricHeater(93);
}
9.静态注入(STATIC INJECTION)
警告:这个特性谨慎使用,静态依赖关系难以测试和重用。
Dagger可以注入静态属性,使用@Inject注解声明静态属性的类必须列在模块的staticInjections 中。
@Module(
staticInjections = LegacyCoffeeUtils.class
)
class LegacyModule {
}
使用ObjectGraph.injectStatics()方法给静态属性填值
ObjectGraph objectGraph = ObjectGraph.create(new LegacyModule());
objectGraph.injectStatics();
注意:静态注入只对直接图中的模块起作用。如果在调用plus()调用图的injectStatics(),则不会执行对扩展图中的模块的静态注入。
10.编译时验证 (COMPILE-TIME VALIDATION)
Dagger包括一个验证模块和注入的注解处理器。此处理器是严格的,如果任何绑定无效或不完整,将导致编译器错误。例如,此模块缺少Executor的绑定:
@Module
class DripCoffeeModule {
@Provides Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
当编译时,javac拒绝缺少的绑定:
通过为Executor添加一个@Provides注解的方法或将模块标记为不完整来修复此问题。不完整的模块允许有缺失的依赖。
@Module(complete = false)
class DripCoffeeModule {
@Provides Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
列出的注入类型未使用的模块会触发错误。
@Module(injects = Example.class)
class DripCoffeeModule {
@Provides Heater provideHeater() {
return new ElectricHeater();
}
@Provides Chiller provideChiller() {
return new ElectricChiller();
}
}
因为在模块中注入Example 只使用Heater,所以javac拒绝没有使用的绑定:
[ERROR] COMPILATION ERROR:
[ERROR]: Graph validation failed: You have these unused @Provider methods:
1. coffee.DripCoffeeModule.provideChiller()
Set library=true in your module to disable this check.
如果模块绑定将在列出的注入对象之外使用,那么将模块标记为库。
@Module(
injects = Example.class,
library = true
)
class DripCoffeeModule {
@Provides Heater provideHeater() {
return new ElectricHeater();
}
@Provides Chiller provideChiller() {
return new ElectricChiller();
}
}
充分利用编译时验证,创建一个包含所有应用程序模块的模块。注解处理器将跨模块检测问题并报告。
@Module(
includes = {
DripCoffeeModule.class,
ExecutorModule.class
}
)
public class CoffeeAppModule {
}
在编译类路径中包含Dagger的jar文件时,将自动启用注解处理器。
11.编译时代码生成(COMPILE-TIME CODE GENERATION)
编译时代码生成Dagger的注解处理器也可以生成名为CoffeeMaker I n j e c t A d a p t e r . j a v a 或 D r i p C o f f e e M o d u l e InjectAdapter.java或DripCoffeeModule InjectAdapter.java或DripCoffeeModuleModuleAdapter的源文件。这些文件是Dagger实现细节。
12.模块重写(MODULE OVERRIDES)
如果对于同一依赖有多个相互矛盾的@Provides方法,Dagger注入失败并出现错误。但是有时有必要用开发或测试的替代品来替代生产代码。在模块注解中使用overrides = true允许优先于其他模块的绑定。
public class CoffeeMakerTest {
@Inject CoffeeMaker coffeeMaker;
@Inject Heater heater;
@Before public void setUp() {
ObjectGraph.create(new TestModule()).inject(this);
}
@Module(
includes = DripCoffeeModule.class,
injects = CoffeeMakerTest.class,
overrides = true
)
static class TestModule {
@Provides @Singleton Heater provideHeater() {
return Mockito.mock(Heater.class);
}
}
@Test public void testHeaterIsTurnedOnAndThenOff() {
Mockito.when(heater.isHot()).thenReturn(true);
coffeeMaker.brew();
Mockito.verify(heater, Mockito.times(1)).on();
Mockito.verify(heater, Mockito.times(1)).off();
}
}
覆盖最适合应用程序的小变化:
用单元测试的模拟代替实际实现。
在开发中使用伪身份验证替换LDAP身份验证。