我可以想到三种方法来做到这一点:儿童注射器、手动 DI 和辅助注射。
儿童注射器
这可能是最好的选择,两年前我的回答中也列出了。
从 Guice 文档中关于“机器人腿问题”的提示中,您可以创建一个子注入器,允许您@NodeNumber int
在树中的任何位置绑定您想要的。这意味着您的绑定和结构不必更改太多,即使过滤器顺序发生变化,或者以后在 C、D、F 或其任何依赖项中需要您的节点号。在您提供 NodeNumber 绑定之前,B、C、D、E 将无法注入,但这对于您所描述的内容可能是正确的。
自然,您可以使用@Named("nodeNumber")
而不是定义@NodeNumber
绑定注释,或者您可以重构为您保存在其他地方的 BFactory。
class E {
@Inject @NodeNumber int nodeNumber; // Available anywhere in your graph!
}
class D { @Inject E e; }
class C { @Inject D d; }
class B { @Inject C c; }
class A {
@Inject Injector injector; // You can always inject the Injector!
B addNode(final int nodeNumber) {
// Create a new injector
return injector.createChildInjector(new AbstractModule() {
@Override public void configure() {
bindConstant().annotatedWith(NodeNumber.class).to(nodeNumber);
}
}).getInstance(B.class);
}
}
手动 DI
如果您只有少数不经常更改的依赖项,则可以手动创建自己的堆栈。这可能无法充分发挥 Guice 的潜力,但很清楚这里发生了什么,以及如果 B/C/D/E 获得或失去任何部门,需要改变什么。
class A {
@Inject Provider<SomeDepOfB> bDep; // If anything in the tree has other deps,
@Inject Provider<SomeDepOfC> cDep; // you'll need to provide them yourself.
@Inject Provider<SomeDepOfD> dDep;
B addNode(int nextNodeNumber) {
return new B(
bDep.get(),
new C(
cDep.get(),
new D(
dDep.get(),
new E(nextNodeNumber))));
}
}
辅助注射
您说您试图避免这种情况,但为了完整起见,我将其列在这里。
class E {
interface Factory { E create(int nodeNumber); }
E(@Assisted int nodeNumber, SomeDep1OfE dep1, SomeDep2OfE dep2) { /* ... */ }
}
class D {
interface Factory { D create(int nodeNumber); }
D(@Assisted int nodeNumber, E.Factory eFactory) { /* ... */ }
}
// ...
class YourModule extends AbstractModule {
@Override public void configure() {
install(new FactoryModuleBuilder().build(E.Factory.class)); // Binds E.Factory
}
}
尽管辅助注入在这里可能不是正确的选择,但它的使用非常简单,并且可以带走很多样板文件(特别是如果 E 有很多其他部门等)。