Dagger1依赖注入


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.javaDripCoffeeModuleModuleAdapter的源文件。这些文件是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身份验证。

猜你喜欢

转载自blog.csdn.net/javahelpyou/article/details/105538856
今日推荐