【问题标题】:Understanding dependency injection in Java with Dagger用 Dagger 理解 Java 中的依赖注入
【发布时间】:2017-04-04 21:59:15
【问题描述】:

我熟悉传统设计模式中的依赖倒置原则,其中您有一个抽象产品,它在请求工厂方法的类中执行任务。但依赖注入的不同之处在于它使用注解来生成产品。

我正在审查这个Dagger 2 示例。这是最终结果:

// CoffeeApp.java
public class CoffeeApp {
  @Singleton
  @Component(modules = { DripCoffeeModule.class })
  public interface Coffee {
    CoffeeMaker maker();
  }

  public static void main(String[] args) {
    Coffee coffee = DaggerCoffeeApp_Coffee.builder().build();
    coffee.maker().brew();
  }
}

$ java -cp ... coffee.CoffeeApp
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
 [_]P coffee! [_]P

我在这里无法理解这一行:

 CoffeeMaker maker();

在 CoffeeMaker 类中,没有 make() 方法。那么这是如何工作的呢?

这是我的理解:

CoffeeMaker 类在其构造函数中使用@Inject 注解:

@Inject CoffeeMaker(Lazy<Heater> heater, Pump pump) {
  this.heater = heater;
  this.pump = pump;
}

这将迫使 Dagger 在实例化 CoffeeMaker 时实例化一个新的 Heater 和一个新的 Pump,对吗?还是只需要引用 CoffeeMaker 类本身?文件是这样说的:

当您请求 CoffeeMaker 时,它会通过调用 new CoffeeMaker() 并设置其可注入字段。

但是请求是什么意思?

当它说,它将设置它的可注入字段,我注意到 Heater 和 Pump 是接口。有一个带有 @Module 注释的对应 PumpModule 类。它包含方法providePump。

文件说:

The method’s return type defines which dependency it satisfies.

这是否意味着当调用@Inject CoffeeMaker 构造函数时,它会依次发现泵引用并搜索PumpModule @Module,然后发现providePump 并实例化一个Thermosiphon 类型的新泵?

【问题讨论】:

标签: java dependency-injection dagger-2


【解决方案1】:

Dagger 是一个编译时依赖注入解决方案。

接口CoffeeApp.Coffee 上的CoffeeMaker maker() 行,按原样注释,意味着Dagger 将生成并编译CoffeeApp.Coffee 的实现,该实现具有maker 方法的实现这将为您提供一个完全注入的 CoffeeMaker 实例。如果您愿意,可以查看 Dagger 生成的文件 DaggerCoffeeApp_Coffee.java,它看起来很像 slide 41 of the original Dagger 2 talk 上的实现。 (幻灯片 35 和 36 显示了 Dagger 编写的相应的 Factory/Provider 实现。)

这将迫使 Dagger 在实例化 CoffeeMaker 时实例化一个新的 Heater 和一个新的 Pump,对吗?

是的,通常。尽管在 @Provides 方法中返回 现有 实例是您的特权,或者使用 @Singleton 之类的注解让 Dagger 管理现有实例,但 Dagger 的默认行为是为每个依赖项,并为该依赖项的每个依赖项创建一个新实例,依此类推。

这是否意味着当调用@Inject CoffeeMaker 构造函数时,它会依次发现泵引用并搜索PumpModule @Module,然后发现providePump 并实例化一个Thermosiphon 类型的新泵?

您几乎是正确的:为了生成该实现,Dagger 将检查 @Inject CoffeeMaker 构造函数,发现 CoffeeMaker 需要加热器和泵,等等。对于像 Guice 这样的框架,这一切都发生在运行时,但对于 Dagger,所有的分析和映射都发生在编译时。在您的示例中,@Provides Pump 方法存在于 DripCoffeeModule 中,Dagger 不会搜索该方法:该示例通过 CoffeeApp.Coffee 上的 @Component 注释提供它。

【讨论】:

    【解决方案2】:

    @Donato,你是对的,CoffeeMaker 没有maker() 方法,而是Coffee 接口有maker() 方法。并且该方法 (maker()) 返回一个 CoffeMaker 类型的对象。因此,在您的示例中,DaggerCoffeeApp_Coffee.builder().build(); 将返回 Coffee 的实现,而后者又实现了 maker() 方法。

    问题:“但请求是什么意思?”

    我的回答:请求意味着注入(即无论你在哪里看到@Inject)。因此,例如,以下私有变量声明“请求”CoffeeMaker 的实例,

    public MyClass {
      @Inject private CoffeeMaker maker;
      ...
    }
    

    而CoffeeMaker 反过来“请求”一个实例或Heater 和Pump 的一个实例:

    @Inject CoffeeMaker(Lazy<Heater> heater, Pump pump) {
      this.heater = heater;
      this.pump = pump;
    }
    

    所以Heater的实例和Pump的实例将在实例化(或注入)maker时被实例化和注入。

    问题: "这是否意味着当调用@Inject CoffeeMaker 构造函数时,它会依次发现泵引用并搜索PumpModule @Module,然后发现providePump 并实例化一个新的热虹吸式泵?"

    我的回答:没错。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-17
      • 2016-07-11
      • 1970-01-01
      相关资源
      最近更新 更多