Dependency injection 使用具有循环依赖项的Guice
考虑这个简单的例子Dependency injection 使用具有循环依赖项的Guice,dependency-injection,guice,Dependency Injection,Guice,考虑这个简单的例子 Class A { B b; A() { this.b = new B(this); } } 在本例中,实例A了解实例B,实例B了解实例A 我的问题是:如何用Guice实例化实例A,即如何让Guice处理这个复杂的循环依赖关系?答案是,当代码中存在循环依赖关系时,不应该使用依赖注入框架 因此,您必须事先重构代码。据我所知,紧密耦合类有两种解决方案:要么将两个类合并为一个类,要么引入新类并将公共逻辑移到其中(以查看细节)回答第一个问题“如何使用
Class A {
B b;
A() {
this.b = new B(this);
}
}
在本例中,实例A了解实例B,实例B了解实例A
我的问题是:如何用Guice实例化实例A,即如何让Guice处理这个复杂的循环依赖关系?答案是,当代码中存在循环依赖关系时,不应该使用依赖注入框架
因此,您必须事先重构代码。据我所知,紧密耦合类有两种解决方案:要么将两个类合并为一个类,要么引入新类并将公共逻辑移到其中(以查看细节)回答第一个问题“如何使用Guice实例化实例”:您可以简单地将
@Inject
添加到构造函数中:
class A {
private final B b;
@Inject
A() {
this.b = new B(this);
}
}
这是因为用于创建A
的API没有循环依赖关系。Guice只要在需要创建或注入A
对象时使用A
构造函数即可
如果您的问题是如何使用Guice创建一个对象,而用于创建该对象的API具有循环依赖关系,请参阅(如Yury的回答中所述)。您的示例根本不是问题,因为您直接构建的是B。但是,如果您想让Guice同时创建A和B,那么其中一个或两个都应该是接口。你可以做:
public interface A { /* skipping methods */ }
public interface B { /* skipping methods */ }
public class AImpl implements A {
private final B b;
@Inject
public AImpl(B b) {
this.b = b;
}
// ...
}
public class BImpl implements B {
private final A a;
@Inject
public BImpl(A a) {
this.a = a;
}
// ...
}
即使
AImpl
和BImpl
被定义为单例,Guice也可以处理这种注入(通过代理)。这在这样一个简单的情况下无论如何都是有效的。。。我想可能有更复杂的循环依赖关系,它无法处理。无论如何,消除循环依赖当然更可取。我认为NamshubWriter的建议不太靠谱。我认为在Guice中,构造函数应该只做一件事:将参数分配到字段中。如果您还需要做其他事情,请将其投入工厂或供应商
在本例中,我们需要为a提供一个提供程序。该提供程序可以直接调用new B(),但随后我们将直接将a耦合到B,这是我们首先试图避免的。因此,我们通过工厂间接创建了B,guice可以通过assistedInject为我们提供工厂。这段代码运行和编译都很好,完全解耦了A和B
在现实场景中,您需要将a和B隐藏在接口后面以利用分离
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryProvider;
public class Try {
public static void main(String[] args) {
System.out.println(
Guice.createInjector(new MyModule()).getInstance(A.class)
);
}
}
class MyModule extends AbstractModule {
public void configure() {
bind(A.class).toProvider(AProvider.class);
bind(IBFactory.class).toProvider(
FactoryProvider.newFactory(IBFactory.class, B.class));
}
}
class A {
B b;
public void setB(B b) {
this.b = b;
}
}
class B {
A a;
@Inject
B(@Assisted A a) {
this.a = a;
}
}
class AProvider implements Provider<A> {
private final IBFactory bFactory;
@Inject
AProvider(IBFactory bFactory) {
this.bFactory = bFactory;
}
public A get() {
A a = new A();
a.setB(bFactory.create(a));
return a;
}
}
interface IBFactory {
public B create(A a);
}
import com.google.inject.AbstractModule;
导入com.google.inject.Guice;
导入com.google.inject.inject;
导入com.google.inject.Provider;
导入com.google.inject.assistedinject.Assisted;
导入com.google.inject.assistedinject.FactoryProvider;
公务舱试驾{
公共静态void main(字符串[]args){
System.out.println(
createInjector(新的MyModule()).getInstance(A.class)
);
}
}
类MyModule扩展了AbstractModule{
public void configure(){
绑定(A.class).toProvider(A.class.);
绑定(IBFactory.class).toProvider(
FactoryProvider.newFactory(IBFactory.class,B.class));
}
}
甲级{
B B;
公屋空置登记册(乙){
这个.b=b;
}
}
B类{
A A;
@注入
B(@A){
这个a=a;
}
}
类AProvider实现提供程序您可以简单地将@Inject添加到A的构造函数中。我猜您的实际类要复杂一些。B是接口吗?除了注射疫苗,它还需要注射什么吗?顺便说一句,让“this”字段转义给构造函数通常是个坏主意。不,B不是接口,而是类。当然,循环依赖性并不好,我可以重构这两个类,但我真正需要的是理解Guice的可行性。对于无参数构造函数,甚至不需要@Inject。这与Yury的代码完全相同。@Inject无效,代码也不会从B中解包A,B是guice的全部要点。是的,这与OP编写的代码相同(使用@Inject可以更清楚地表明guice使用了构造函数)。关键是API中没有循环依赖关系,所以Guice可以创建一个循环。这个设计是否是一个好的设计是另一个问题。你可以在我对OP的评论中看到我的一些担忧,OP同意循环依赖是个坏主意。这只适用于单例。一般情况如何?关于注射后的情况有什么想法吗?同意尤里的说法。使用DI的循环依赖会导致痛苦。