Java 如何使用Guice创建递归对象图?
假设我有5个类A、B、C、D、E,它们都实现了一个公共接口X。每个类B、C、D和E都有一个类型为X的字段(因此它们可以被视为包装类) 创建哪些实例是在运行时确定的,因此我可以使用以下对象图之一:Java 如何使用Guice创建递归对象图?,java,guice,Java,Guice,假设我有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创建这个对象,以避免手动提供所有其他依赖项 最简单的方法是什么?目前看来,我不得不这样做
E -> D -> C -> B -> A
D -> B -> A
E -> A
A
(顺序是固定的,最里面的实例总是类型A,但在其他方面没有限制。)
不,我想用Guice创建这个对象,以避免手动提供所有其他依赖项
最简单的方法是什么?目前看来,我不得不这样做
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
注释。我已经尝试过类型侦听器,但还没有找到一种方法来实现这一点。欢迎提供提示。“运行时”增加了相当多的复杂性。我假设这意味着,对于创建的每个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;
}
}
classxfactory{
@注入提供者a;
@注入提供者b;
@注入提供者c;
@注入提供者d;
@注入提供者e;
公共X创建(数据状态){
//“state”对象就是您用来
//决定你需要创建什么对象;
X结果=a.get();
if(state.useB()){
B head=B.get();
head.setNext(结果);
结果=头;
}
if(state.useC()){
C head=C.get();
head.setNext(结果);
结果=头;
}
if(state.useD()){
D head=D.get();
head.setNext(结果);
结果=头;
}
if(state.useE()){
E head=E.get();
head.setNext(结果);
结果=头;
}
返回结果;
}
}
可以考虑引入一个接口,如CueAccess X或WrApPrX来保存SETNEXT()方法。这可能会减少一些重复
此外,如果“在运行时”实际上是应用程序启动,因此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
}
}
MyModule类扩展了AbstractModule{
public void configure(){
多粘合剂链
=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);
}
@提供X个provideX(集合链,A){
X结果=a;
//“项目”是B,然后是C。。。
用于(X项:链){
项目.设置下一步(结果);
结果=项目;
}
返回结果;//这是E
}
}
我知道这个答案相当抽象,但我希望它能让您更接近解决方案。我还发现您定义中的“运行时”部分不清楚
但假设我们从想象这些链条是如何在没有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());
}
}
//此处为伪多类定义
B类…E类扩展X类{
私人最终X nextInChain;
@注入
公共B…E(前文无需提及,X下一条){
超级(同样需要);
this.nextInChain=nextInChain;
}
}
A类扩展X{
@注入
公共A(上述无需提及该办公室){
超级(同样需要);
}
}
类锚链架{
公共最终X链1;
公开决赛X链2;
公开决赛X链3;
公开决赛X链4;
@注入
公共链架(@Chain1 X c1、@Chain2 X c2、@Chain3 X c3、@Chain4 X c4){
链1=c1;
链2=c2;
链3=c3;
链4=c4;
}
}
类chainModlule扩展了AbstractModule{
public void configure(){
//你想要的其他东西都在这里。
bind(thestuff needtoo.class).to(thestuff未在问题.class中提及);
//也许A应该始终是同一个终止实例。
bind(A).in(Singleton.class);
将(X).annotatedWith(Chain4.class.)绑定到(A.class);
}
@提供@Chain1
public X providesChain1X(@Chain1 Provider e){
返回e.get();
}
@提供@Chain1
public E providesChain1E(@Chain1 Provider d,thestuffiedtoo stuff){
返回新的E(stuff,d.get());
}
@提供@Chain1
public D providesChain1D(@Chain1 Provider c,thestuffiedtoo stuff){
返回新的D(stuff,c.get());
}
@提供@Chain1
public C C providesChain1C(@Chain1 Provider b,thestuffiedtoo stuff){
返回新的C(stuff,b.get());
}
@提供@Chain1
公共B提供链1B(提供程序a,邮局需要太多东西){
返回新的B(stuff,a.get());
}
//…与第2链类似,然后。。。
@提供@Chain3
公共X
class B {
B(@InInstance(B.class) X x,
Other dependencies) {
...
}
}
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);