Guice 辅助注入更深入地了解依赖关系层次结构

2022-09-04 22:33:06

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

  • interface A实现方式需要一些输入class AImpl
  • interface B按需求实施class BImplA
  • interface C按需求实施class CImplB
  • interface D按需求实施class DImplC

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

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

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

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

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


答案 1

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

花哨的方式:在 中,请指 a,但不要将其绑定到主注射器中。否则,请像往常一样绑定依赖项,包括在其他管道相关类中引用依赖项。当你确实需要 a 时,从你的实现中获取它,我称之为 。A@PipelineInput String inputString@PipelineInputDDFactoryPipelineRunner

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);
  }
}

当然,对于 、 、 和 的绑定尝试将因缺少而失败 - 当您使用那些不满意的依赖项创建注入器时,您将获得一个,正如您所发现的那样 - 但是这些基于管道的依赖项应该很容易分离到您安装到子注入器中的模块中。ABCDPipelineRunner@PipelineInput StringCreationException

如果这感觉太麻烦了,请记住,PrivateModules也是“使用父注入器实现的”,并且依赖注入的全部意义在于以解耦的方式使依赖关系可用于整个对象图。inputForA


答案 2

我看到三种选择。它们取决于您更改 for 的频率。inputA

1) 在模块中绑定为常量。这仅适用于您在创建之前知道该值并且永远不想更改该值的情况。请参阅 bindConstantinputInjector

2) 使用私有子模块,该子模块绑定该模块或该模块内部的值。基本上,您可以拥有两个或三个具有不同值的实例图。请参阅 newPrivateBinderAinput

3) 使用阿拉 、 、 ...通过这种方式,您可以经常更改输入,但必须在要定义的某个时间点输入/离开范围。有关示例,请参阅自定义范围ScopeRequestScopeSessionScope


推荐