13

我想进行一系列处理元素并通过 Guice 将它们连接在一起。让我们假设以下路径:

  • interface Aclass AImpl需要一些输入来实现
  • interface Bclass BImpl需要执行A
  • interface Cclass CImpl需要执行B
  • interface Dclass DImpl需要执行C

A 的依赖关系只能在运行时解决,而不能在配置时解决。通常的方法是在这种情况下使用辅助注入来创建一个工厂,它将丢失的实例作为参数,就像这样:

public interface AFactory {
    public A createA(String input);
}

但我真正想要的是这样的:

public interface DFactory {
    public D createD(String inputForA);
}

我不想AImpl在整个层次结构中手动传递特定的依赖项。是否有可能使用 Guice 实现这一目标?如果不是,那么在保持注入优势的同时优雅地规避这个问题的最佳方法是什么?

4

2 回答 2

7

作弊方式:坚持input使用静态变量或单例ThreadLocal。在管道开始之前设置它并在它结束后清除它。通过 DI 绑定其他所有内容。

花哨的方式:A中,引用 a@PipelineInput String inputString但不要将其绑定在主注入器中。否则,像往常一样绑定依赖项,包括@PipelineInput在其他与管道相关的类中引用。当您确实需要 aD时,请从您的 a 实现中获取它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);
  }
}

自然,对A, B,C和的绑定尝试DPipelineRunner因为缺少 a而失败——正如你所发现的那样,当你使用那些不满足的依赖项创建注入器时,你@PipelineInput String会得到 a CreationException——但是这些基于管道的依赖项应该很容易分离到您安装到子注入器中的模块中。

如果这感觉太 hacky,请记住 PrivateModules 也是“使用父注入器实现的”,并且依赖注入inputForA的全部目的是以解耦的方式使整个对象图都可以使用依赖。

于 2013-05-02T06:38:37.280 回答
1

我看到三个选项。它们取决于您更改inputfor 的频率A

1)绑定input为模块中的常量。仅当您在创建之前知道该值Injector并且不想更改该值时,这才有效。看bindConstant

2)使用一个私有子模块,该子模块在该模块内绑定其中一个A或值input。基本上你可以有两个或三个具有不同值的实例图。请参阅newPrivateBinder

3) 使用Scopeala RequestScope, SessionScope, ... 这样您可以经常更改输入,但必须在某个点进入/离开要定义的范围。有关示例,请参阅自定义范围。

于 2013-05-01T13:00:29.103 回答