考慮這個簡單的例子。使用吉斯與循環依賴
Class A {
B b;
A() {
this.b = new B(this);
}
}
在這個例子中實例的知道實例B,和實例B知道有關實例A.
我的問題是:如何實例化實例的與吉斯,即如何讓吉斯照顧這個複雜的循環依賴關係?
考慮這個簡單的例子。使用吉斯與循環依賴
Class A {
B b;
A() {
this.b = new B(this);
}
}
在這個例子中實例的知道實例B,和實例B知道有關實例A.
我的問題是:如何實例化實例的與吉斯,即如何讓吉斯照顧這個複雜的循環依賴關係?
要回答第一個問題「如何實例化例如A和吉斯「:你可以簡單地添加@Inject
的構造器:
class A {
private final B b;
@Inject
A() {
this.b = new B(this);
}
}
這工作,因爲創建A
API不具有循環依賴。 Guice只需要使用A
構造函數來創建或注入一個A
對象。
如果您的問題是如何使用Guice創建一個對象,其中創建對象的API具有循環依賴關係,請參閱this blog post by Misko Hevery(如Yury的答案中所述)。
您甚至不需要@Inject作爲無參數構造函數。 – ColinD
這與Yury的代碼完全相同。 @Inject不起作用,並且代碼不會從B中加A,這是guice的全部要點。 – nes1983
是的,這與OP所寫的代碼相同(使用@Inject使其更清楚,Guice使用該構造函數)。問題在於API沒有循環依賴,所以Guice可以創建A. 這個設計是否是一個好設計是一個不同的問題。您可以在我對OP的評論中看到我的一些擔憂,並且OP同意循環依賴不是一個好主意。 – NamshubWriter
答案是你不應該使用依賴注入框架,而你的代碼中有循環依賴。
所以,你必須事先重構你的代碼。據我所知,緊耦合類有兩種解決方案:將兩個類合併爲一個類或引入新類並將常見邏輯移入其中(詳細信息請參見here)
我認爲你需要重讀大膽的部分:) –
同意Yury。與DI有關的循環依賴會導致疼痛。 –
你舉的例子不是一個問題了,因爲你直接構建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
和作用域爲singleton,吉斯可以處理這種注射(通過代理方式)。這在任何情況下都可以在這種簡單的情況下工作......我想可能會有更復雜的循環依賴關係,它無法處理。無論如何,消除循環依賴將是更可取的,當然。
這真的只適用於單身人士。一般情況如何? – nes1983
關於此次注射現場背後發生的任何想法? –
我認爲NamshubWriter的建議是不是很guicy。我認爲在Guice中,構造函數應該完成一件事:將參數分配到字段中。如果您還需要做其他事情,請將其放入工廠或供應商處。
在這種情況下,我們想爲A.)供應商的供應商可以直接調用new B(,但隨後我們會直接耦合A到B,而這正是我們試圖避免在首位。因此,我們間接在工廠創建B,這可以通過assistInject爲我們提供。此代碼運行和編譯好,並完全解耦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);
}
您可以簡單地添加@Inject的構造方法A.我猜你的實際類是有點複雜。 B是一個界面嗎?是否需要注入A以外的東西? 順便說一句,讓「這個」字段轉義構造函數通常是一個壞主意。 – NamshubWriter
不,B不是一個接口,而是一個類。 當然,循環依賴不好,我可以重構這兩個類,但我真正需要的是理解Guice的可行性。 –