Guice在依赖层次结构中更深入地协助注入
我想进行一系列处理元素并通过Guice将它们连接在一起。 我们假设以下路径:
-
interface A
由class AImpl
实现的interface A
需要一些输入 -
class BImpl
实现的interface B
需要A
- 由
class CImpl
实现的interface C
需要B
- 由
class DImpl
实现的interface D
需要C
A的依赖关系只能在运行时解决,而不能在配置时解决。 通常的方法是在这种情况下使用Assisted Injection来创建一个工厂,它将缺少的实例作为参数,如下所示:
public interface AFactory { public A createA(String input); }
但我真正想要的是这样的:
public interface DFactory { public D createD(String inputForA); }
我不想在整个层次结构中手动传递AImpl
特定的依赖项。 是否可以通过Guice实现这一目标? 如果没有,那么在保留注射效益的同时优雅地避免这个问题的最佳方法是什么?
作弊方式:将 input
粘贴在静态变量或单例ThreadLocal
。 在管道启动之前设置它并在它结束后清除它。 通过DI绑定其他所有内容。
花哨的方式:在A
,引用@PipelineInput String inputString
但不要在主注入器中绑定它。 否则,像往常一样绑定依赖项,包括在其他管道相关类中引用@PipelineInput
。 当你需要一个D
,从你的DFactory
实现中获取它,我称之为PipelineRunner
。
public class PipelineRunner { @Inject Injector injector; // rarely a good idea, but necessary here public D createD(final String inputForA) { Module module = new AbstractModule() { @Override public void configure() { bindConstant(inputForA).annotatedWith(PipelineInput.class); } }; return injector.createChildInjector(new PipelineModule(), module) .getInstance(D.class); } }
当然,由于缺少@PipelineInput String
, A
, B
, C
和D
绑定尝试将在PipelineRunner
之外失败 – 当您创建具有那些不满足的依赖关系的注入器时,您将获得CreationException
– 正如您所发现的那样 – 但是那些基于管道的依赖项应该很容易分成您安装到子注入器中的模块。
如果这感觉太过苛刻,请记住,PrivateModules也是“ 使用父注入器实现的 ”,并且dependency injection的整个点是使得像inputForA
这样的依赖关系以分离的方式可用于整个对象图。
我看到三种选择。 它们取决于您更改A
的input
频率。
1)将input
绑定为模块中的常量。 如果您在创建Injector
之前知道该值并且从不想更改该值,则此方法仅适用。 请参见bindConstant
2)使用私有子模块,该子模块绑定A
或该模块内的input
值。 基本上,您可以有两个或三个具有不同值的实例图。 请参阅newPrivateBinder 。
3)使用Scope
ala RequestScope
, SessionScope
,…这样您可以经常更改输入,但必须在某个点定义进入/离开范围。 有关示例,请参阅自定义范围 。