考虑这个简单的例子。
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 处理这种复杂的循环依赖关系?
您的示例根本不是问题,因为您正在直接构建 B 。但是如果你希望 A 和 B 都由 Guice 创建,一个或两个应该是一个接口。你可以做:
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);
}
要回答您的第一个问题“如何使用 Guice 实例化实例 A”:您可以简单地添加@Inject
到构造函数中:
class A {
private final B b;
@Inject
A() {
this.b = new B(this);
}
}
这是有效的,因为用于创建的 APIA
没有循环依赖。Guice 只会在需要创建或注入对象的A
任何时候使用构造函数。A
如果您的问题是如何使用 Guice 创建对象,其中用于创建对象的 API 具有循环依赖关系,请参阅Misko Hevery 的这篇博文(如 Yury 的回答中所述)。