java - 如何使用 Guice 创建递归对象图?

标签 java guice

假设我有 5 个类 A、B、C、D、E,它们都实现了一个公共(public)接口(interface) X。每个类 B、C、D 和 E 都有一个类型为 X 的字段(因此它们可以看作是包装器类)。

创建哪些实例是在运行时确定的,因此我可以拥有例如以下对象图之一:

E -> D -> C -> B -> A
D -> B -> A
E -> A
A

(顺序是固定的,最里面的实例总是类型A,除此之外没有限制。)

不,我想用 Guice 创建这个对象以避免手动提供所有其他依赖项。

最简单的方法是什么?目前看来我必须

  1. 让 Guice 创建 A 的实例
  2. 创建一个将 X.class 绑定(bind)到这个实例的模块和一个带有这个附加模块的子注入(inject)器
  3. 让 Guice 创建下一个实例(例如类型 B)
  4. 重复 2.,现在将 X.class 绑定(bind)到 B 的实例
  5. 重复 3. 和 4. 直到创建所有对象

有没有更简单的方法?我是否可以在每次创建 X 时以某种方式自动注册 X 子类的新实例作为 X 的绑定(bind)?

编辑:澄清“创建哪些实例是在运行时确定的”:

我当前的代码如下所示:

X createX() {
  X x = new A(dependencies);
  if (useB) x = new B(x, some, dependencies);
  if (useC) x = new C(x, further, dependencies);
  if (useD) x = new D(x, dependency);
  if (useE) x = new E(x, yet, other, dependencies);
  return x;
}

标志 useB、useC、useD 和 useE 的值来自属性文件。

我的主要目标是避免手动向构造函数提供所有依赖项。

编辑:解决方案:

我已经添加了我自己的解决方案,这是我同时找到的。感谢所有回答者!

改进我的解决方案的一种方法是可以删除构造函数参数上的 @InInstance 注释。我已经尝试过类型监听器,但我还没有找到执行此操作的方法。欢迎提供提示。

最佳答案

我针对这个问题制定了以下解决方案:

首先,我创建了一个标记注解 @InInstance,它将一个 Class 对象作为值。我使用这个注释来注释所有包装器类(即 B 到 E)中类型 X 的参数。示例:

class B {
  B(@InInstance(B.class) X x,
    Other dependencies) {
  ...
  }
}

现在,如果我想要一个完整链(A 到 E)的实例,我将绑定(bind)

  • X级到E级
  • X.class annotatedWith InInstance(E.class) to D.class
  • ...
  • X.class annotatedWith InInstance(B.class) to A.class

然后调用createInstance(X.class)

这解决了让 Guice 创建我的实例并提供依赖项的问题。

现在,对于根据属性文件在运行时创建适当绑定(bind)的问题,我创建了一个 Guice 扩展,允许我创建“包装器绑定(bind)”。这样的绑定(bind)需要一个类型(这里是 X)、一个包装器实现类型(这里是 B 到 E 之一)和一个基础模块,它需要包含 X 的绑定(bind)。然后它创建一个包含两个绑定(bind)的新模块:

  • X 的绑定(bind)与基础模块中的绑定(bind)相同,但 annotatedWith(InInstance(wrapper type))
  • 从 X 到包装器类型的绑定(bind)

然后可以使用这样的模块来覆盖基本模块的绑定(bind) Modules.override(base module).with(wrapper module)

使用一些不错的 Guice 风格的 EDSL,这看起来像:

Module baseModule = ... // contains binding for X to A

if (useB) {
  Module wrapperModule = new ChainingModule() {
    void configure() {
      wrap(X.class).from(baseModule).with(B.class); // possible to use .in(...) etc. here
      // In this case, this line is equivalent to

      // bind(X.class).annotatedWith(InInstance(B.class)).to(A.class);
      // bind(X.class).to(B.class);

      // It has the advantage of automatically looking up the current binding
      // for X in the base module, which makes it easy to chain this.
    }
  }
  baseModule = Modules.override(baseModule).with(wrapperModule);
}

...

还有一些常见情况的辅助方法,它看起来像:

Module module = ... // contains binding for X to A
if (useB) {
  module = Chaining.wrap(X.class).from(module).with(B.class);
}
if (useC) {
  module = Chaining.wrap(X.class).from(module).with(C.class);
}
if (useD) {
  module = Chaining.wrap(X.class).from(module).with(D.class);
}
if (useE) {
  module = Chaining.wrap(X.class).from(module).with(E.class);
}

Injector injector = Guice.createInjector(module);
X x = injector.createInstance(X.class);

每一行都创建适当的包装器模块并返回一个新模块,其中包含给定模块以及包装器模块覆盖的一些绑定(bind)。

关于java - 如何使用 Guice 创建递归对象图?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7416974/

相关文章:

java - Spring 和 Guice 一起,或者只是 Spring

java - 我应该在方法参数中还是在 Java 的父构造函数中传递对象

java - XML 目录文件无法解析

java - 在 Java 字节码中获取堆栈操作数的 arrayref

java - 将远程机器注册为节点在网格控制台上提供连接超时

java - Guice 如何填充带注释的字段

gwt - 谷歌 GIN AbstractModule 到 GET.Create()

java - 配置类 - Guice 的最佳实践

java - Java中如何获取一个新的指针?

java - 返回第 n 个 child