假设我有5个类A,B,C,D,E,它们都实现了一个公共接口X.每个类B,C,D和E都有一个X类型的字段(所以它们可以看作是包装器类)。
创建的实例是在运行时确定的,因此我可以使用以下对象图之一:
E -> D -> C -> B -> A
D -> B -> A
E -> A
A
(订单是固定的,最里面的实例总是A型,但是没有限制。)
不,我想用Guice创建这个对象,以避免手工提供所有其他依赖项。
最简单的方法是什么?目前似乎我必须
有更简单的方法吗?我是否可能以某种方式在每次创建时自动注册X的子类的新实例作为X的绑定?
编辑:澄清“在运行时确定创建了哪些实例”:
我目前的代码如下:
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
注释。我已经尝试过类型监听器,但我还没有找到办法。欢迎提示。
答案 0 :(得分:4)
我已经针对这个问题开发了以下解决方案:
首先,我创建了一个标记注释@InInstance
,它将Class
个对象作为值。我使用这个注释来注释所有包装类中的类型X的参数(即B到E)。例如:
class B {
B(@InInstance(B.class) X x,
Other dependencies) {
...
}
}
现在,如果我想要一个完整链的实例(A到E),我绑定
然后拨打createInstance(X.class)
。
这解决了让Guice创建我的实例并提供依赖关系的问题。
现在针对根据属性文件在运行时创建适当绑定的问题,我创建了一个Guice扩展,允许我创建“包装器绑定”。这样的绑定需要一个类型(这里是X),一个包装器实现类型(这里是B到E之一)和一个基本模块,它需要包含一个X的绑定。然后它创建一个包含两个绑定的新模块:
annotatedWith(InInstance(wrapper type))
然后可以使用这样的模块来覆盖基础模块与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);
每一行创建适当的包装器模块并返回一个包含给定模块的新模块,其中一些绑定由包装器模块覆盖。
答案 1 :(得分:1)
"在运行时"增加了相当多的复杂性。我认为这意味着您创建的每个 X实例,您可以对包裹A的对象链做出一些决定。
如果你不将X注入E,D,C& B,施工期间?而是添加setNext(X)方法。我不确定这对你有多现实,但请听我说。您可以创建如下工厂:
class XFactory {
@Inject Provider<A> a;
@Inject Provider<B> b;
@Inject Provider<C> c;
@Inject Provider<D> d;
@Inject Provider<E> e;
public X create(Data state) {
// the 'state' object is just whatever information you use to
// decide what objects you need to create;
X result = a.get();
if(state.useB()) {
B head = b.get();
head.setNext(result);
result = head;
}
if(state.useC()) {
C head = c.get();
head.setNext(result);
result = head;
}
if(state.useD()) {
D head = d.get();
head.setNext(result);
result = head;
}
if(state.useE()) {
E head = e.get();
head.setNext(result);
result = head;
}
return result;
}
}
您可以考虑引入DelegatingX或WrapperX等接口来保存setNext()方法。它可能会减少一些重复。
另外,如果&#34;在运行时&#34;实际上是应用程序启动,因此X的每个实例应该是同一个对象链,然后你有一个额外的选项:
class MyModule extends AbstractModule {
public void configure() {
Multibinder<DelegatingX> chain
= Multibinder.newSetBinder(binder(), DelegatingX.class);
if(useB()) chain.addBinding().to(B.class);
if(useC()) chain.addBinding().to(C.class);
if(useD()) chain.addBinding().to(D.class);
if(useE()) chain.addBinding().to(E.class);
}
@Provides X provideX(Set<DelegatingX> chain, A a) {
X result = a;
// 'item' is B, then C...
for(X item : chain) {
item.setNext(result);
result = item;
}
return result; // this is E
}
}
我知道这个答案非常抽象,但我希望它能让你更接近解决方案。
答案 2 :(得分:1)
我还发现你的定义“运行时”部分不清楚。
但是,我们假设我们首先想象的是如何在没有Guice的情况下制作这些链条。
class chainHolder {
public final X chain1 = new E(new D(new C(new B(new A()))));
public final X chain2 = new D(new B(new A()));
public final X chain3 = new E(new A());
public final X chain4 = new A();
}
但是你的问题是,你想要注入的每个构造函数中还有很多其他东西要传递给你吗?好吧,让我们为这些设置一些注入:
//Pseudo multi-class def here
classes B...E extends X {
private final X nextInChain;
@Inject
public B...E(TheStuffINeedToo NotMentionedAbove, X nextInChain) {
super(TheStuffINeedToo);
this.nextInChain = nextInChain;
}
}
class A extends X {
@Inject
public A(TheStuffINeedToo NotMentionedAbove) {
super(TheStuffINeedToo);
}
}
class chainHolder {
public final X chain1;
public final X chain2;
public final X chain3;
public final X chain4;
@Inject
public chainHolder(@Chain1 X c1, @Chain2 X c2, @Chain3 X c3, @Chain4 X c4) {
chain1 = c1;
chain2 = c2;
chain3 = c3;
chain4 = c4;
}
}
class chainModlule extends AbstractModule {
public void configure() {
//The other stuff you wanted provided gets bound here.
bind(TheStuffINeedToo.class).to(TheStuffNotMentionedInTheQuestion.class);
//Maybe A should always be the same terminating instance.
bind(A).in(Singleton.class);
bind(X).annotatedWith(Chain4.class).to(A.class);
}
@Provides @Chain1
public X providesChain1X(@Chain1 Provider<E> e) {
return e.get();
}
@Provides @Chain1
public E providesChain1E(@Chain1 Provider<D> d, TheStuffINeedToo stuff) {
return new E(stuff, d.get());
}
@Provides @Chain1
public D providesChain1D(@Chain1 Provider<C> c, TheStuffINeedToo stuff) {
return new D(stuff, c.get());
}
@Provides @Chain1
public C providesChain1C(@Chain1 Provider<B> b, TheStuffINeedToo stuff) {
return new C(stuff, b.get());
}
@Provides @Chain1
public B providesChain1B(Provider<A> a, TheStuffINeedToo stuff) {
return new B(stuff, a.get());
}
// ... Similarly for chain2 then ...
@Provides @Chain3
public X providesChain3X(@Chain3 Provider<E> e) {
return e.get();
}
@Provides @Chain3
public E providesChain3E(Provider<A> a, TheStuffINeedToo stuff) {
return new E(stuff, a.get());
}
}
现在。你看,这不是保存/很多/工作。如果所有类都接受了你希望注入为你做的相同的东西(比如我的伪设置),那么你可以将整个链转移到一个提供者并重用那个东西实例,或者提供东西。