2009-09-26 48 views
8

考慮這個簡單的例子。使用吉斯與循環依賴

Class A { 
    B b; 
    A() { 
     this.b = new B(this); 
    } 
} 

在這個例子中實例的知道實例B,和實例B知道有關實例A.

我的問題是:如何實例化實例的與吉斯,即如何讓吉斯照顧這個複雜的循環依賴關係?

+0

您可以簡單地添加@Inject的構造方法A.我猜你的實際類是有點複雜。 B是一個界面嗎?是否需要注入A以外的東西? 順便說一句,讓「這個」字段轉義構造函數通常是一個壞主意。 – NamshubWriter

+0

不,B不是一個接口,而是一個類。 當然,循環依賴不好,我可以重構這兩個類,但我真正需要的是理解Guice的可行性。 –

回答

4

要回答第一個問題「如何實例化例如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的答案中所述)。

+2

您甚至不需要@Inject作爲無參數構造函數。 – ColinD

+0

這與Yury的代碼完全相同。 @Inject不起作用,並且代碼不會從B中加A,這是guice的全部要點。 – nes1983

+0

是的,這與OP所寫的代碼相同(使用@Inject使其更清楚,Guice使用該構造函數)。問題在於API沒有循環依賴,所以Guice可以創建A. 這個設計是否是一個好設計是一個不同的問題。您可以在我對OP的評論中看到我的一些擔憂,並且OP同意循環依賴不是一個好主意。 – NamshubWriter

4

答案是你不應該使用依賴注入框架,而你的代碼中有循環依賴

所以,你必須事先重構你的代碼。據我所知,緊耦合類有兩種解決方案:將兩個類合併爲一個類或引入新類並將常見邏輯移入其中(詳細信息請參見here

+4

我認爲你需要重讀大膽的部分:) –

+0

同意Yury。與DI有關的循環依賴會導致疼痛。 –

8

你舉的例子不是一個問題了,因爲你直接構建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; 
    } 
    // ... 
} 

即使AImplBImpl和作用域爲singleton,吉斯可以處理這種注射(通過代理方式)。這在任何情況下都可以在這種簡單的情況下工作......我想可能會有更復雜的循環依賴關係,它無法處理。無論如何,消除循環依賴將是更可取的,當然。

+0

這真的只適用於單身人士。一般情況如何? – nes1983

+0

關於此次注射現場背後發生的任何想法? –

3

我認爲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); 
} 

I made an extended version of the circular dependency injection in Guice where A and B are hidden behind interfaces.