この簡単な例を考えてみましょう。
Class A {
B b;
A() {
this.b = new B(this);
}
}
この例では、インスタンスAはインスタンスBについて知っており、インスタンスBはインスタンスAについて知っています。
私の質問は、インスタンスAをGuiceでインスタンス化する方法、つまりGuiceにこの複雑な円の依存関係を処理させる方法です。
この簡単な例を考えてみましょう。
Class A {
B b;
A() {
this.b = new B(this);
}
}
この例では、インスタンスAはインスタンスBについて知っており、インスタンスBはインスタンスAについて知っています。
私の質問は、インスタンスAをGuiceでインスタンス化する方法、つまり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はこのインジェクションを(プロキシ経由で)処理できます。これはとにかくこのような単純なケースで機能します...処理できないより複雑な循環依存関係がある可能性があると思います。とにかく、もちろん、循環依存関係を排除することが望ましいでしょう。
答えは、コードに循環依存性がある間は、依存性注入フレームワークを使用すべきではないということです。
したがって、事前にコードをリファクタリングする必要があります。私の知る限り、密結合クラスには2つの解決策があります。2つのクラスを1つにマージするか、新しいクラスを導入して共通論理をその中に移動します(詳細はこちらをご覧ください) 。
NamshubWriterの提案はあまり派手ではないと思います。Guiceでは、コンストラクターは1つのことを実行する必要があると思います。それは、パラメーターをフィールドに割り当てることです。他にやらなければならないことがあれば、それを工場かプロバイダーに入れてください。
この場合、Aのプロバイダーが必要になります。プロバイダーは新しい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);
}
}
これは、作成用のAPIにA
循環依存関係がないために機能します。Guiceは、オブジェクトA
を作成または挿入する必要があるときはいつでもコンストラクターを使用しA
ます。
Guiceを使用して、オブジェクトを作成するためのAPIが循環依存関係にあるオブジェクトを作成する方法について質問がある場合は、Misko Heveryによるこのブログ投稿を参照してください(Yuryの回答に記載されています)。