使用Dagger在构造函数上进行dependency injection

所以,我现在正在重新devise一个我的Android应用来使用Dagger 。 我的应用程序非常庞大复杂,最近遇到以下情况:

对象A需要一个特殊的DebugLogger实例,这是一个完美的注入候选者。 而不是绕过logging器,我可以通过A的构造函数注入它。 这看起来像这样:

class A { private DebugLogger logger; @Inject public A(DebugLogger logger) { this.logger = logger; } // Additional methods of A follow, etc. } 

到目前为止这是有道理的。 然而,A需要由另一个类B构造。必须构造A的多个实例,所以按照Dagger的做法,我简单地将一个Provider<A>注入到B:

 class B { private Provider<A> aFactory; @Inject public B(Provider<A> aFactory) { this.aFactory = aFactory; } } 

好的,到目前为止。 但是等等,突然间A需要额外的input,比如一个叫做“量”的整数,这对于它的构build至关重要。 现在,我的构造函数需要如下所示:

 @Inject public A(DebugLogger logger, int amount) { ... } 

突然间,这个新的参数干扰注射。 而且,即使这样做确实有效,除非我误解,否则从提供者那里检索新实例时,我无法通过“数量”。 有几件事我可以在这里做,而我的问题是哪一个是最好的?

我可以通过添加一个setAmount()方法重构A,这个方法有望在构造函数之后被调用。 然而,这很丑陋,因为它迫使我延迟A的build设,直到“金额”被填补。如果我有两个这样的参数,“数量”和“频率”,那么我会有两个二进制,这意味着复杂的检查,以确保A的构build在所有的调用者被调用后恢复,否则我将不得不添加第三种方法,如下所示:

 (Somewhere in B): A inst = aFactory.get(); inst.setAmount(5); inst.setFrequency(7); inst.doConstructionThatRequiresAmountAndFrequency(); 

另一种select是我不使用基于构造函数的注入并使用基于字段的注入。 但现在,我必须公开我的领域。 这对我来说并不好,因为现在我有义务向其他class级透露我class的内部数据。

到目前为止,我能想到的唯一有点优雅的解决scheme是对提供者使用基于字段的注入,如下所示:

 class A { @Inject public Provider<DebugLogger> loggerProvider; private DebugLogger logger; public A(int amount, int frequency) { logger = loggerProvider.get(); // Do fancy things with amount and frequency here ... } } 

即使如此,我还是不确定时机,因为我不确定在构造函数被调用之前Dagger是否会注入提供者。

有没有更好的办法? 我只是错过了关于匕首如何工作?

Solutions Collecting From Web of "使用Dagger在构造函数上进行dependency injection"

你正在谈论的是被称为辅助注射,目前不被匕首以任何自动方式支持。

你可以用工厂模式解决这个问题:

 class AFactory { @Inject DebugLogger debuggLogger; public A create(int amount, int frequency) { return new A(debuggLogger, amount); } } 

现在你可以注入这个工厂并使用它创buildA实例:

 class B { @Inject AFactory aFactory; //... } 

当你需要用你的“数量”和“频率”来创build一个A ,你使用工厂。

 A a = aFactory.create(amount, frequency); 

这允许A具有logging器,数量和频率字段的final实例,同时仍然使用注入来提供logging器实例。

Guice有一个辅助注入插件,可以为您自动创build这些工厂。 在Dagger邮件列表上讨论了关于添加它们的适当方式,但在撰写本文时没有任何决定。

杰克的post说的是完全正确的。 也就是说,我们(一些与Guice和Dagger一起工作的Google人)正在开发一种替代版本的“辅助注入”或自动工厂生成,可以使用Guice或Dagger或单独使用 – 也就是说,会为您生成工厂类的源代码。 这些工厂类(如果合适的话)可以像任何标准的JSR-330类一样注入。 但尚未公布。

等待这样的解决scheme,杰克·沃顿的方法是可取的。

你有一个问题,因为你在你的构造函数中混合注射剂和非注射剂。 注射的一般规则将为您节省大量的心痛,并保持您的代码清洁:

  1. 注射剂可以要求其他注射剂在他们的构造,但不是新手。

  2. Newables可以在构造函数中请求其他newable,但不能用于injectables。

注入是服务types的对象,也就是说像CreditCardProcessor,MusicPlayer等那样工作的对象。

Newables是值types的对象,如CreditCard,Song等

杰克的post是伟大的,但有更简单的方法。 Google在编译时创build了自动创build工厂的AutoFactory库。

首先,使用@AutoFactory注解创build类A ,并使用@Provided注释来注入参数:

 @AutoFactory public class A { private DebugLogger logger; public A(@Provided DebugLogger logger, int amount, int frequency) { this.logger = logger; } } 

然后库在编译时创buildAFactory类。 所以你只需要将工厂注入到B类的构造函数中。

 public class B { private final AFactory aFactory; @Inject public B(AFactory aFactory) { this.aFactory = aFactory; } public A createA(int amount, int frequency) { return aFactory.create(amount, frequency); } }