13

起動時にコードで次のエラーが発生します。

循環依存関係をサポートするために com.bar.Foo をプロキシしようとしましたが、それはインターフェイスではありません。

このプロキシはどのように機能しますか? インターフェイスの背後に十分なクラスを投げるだけで、すべてうまくいくでしょうか?

(通常、循環依存はコードのにおいがすることはわかっていますが、この場合は問題ないと思います。)

4

3 に答える 3

13

「インターフェイスを挿入する」アプローチは完全に有効であり、場合によってはより良いソリューションになることもありますが、一般的には、より単純なソリューションであるプロバイダーを使用できます。

guice が管理できるすべてのクラス "A" に対して、guice は " Provider<A>" も提供します。これは javax.inject.Provider インターフェースの内部実装であり、そのget()メッセージは " return injector.getInstance(A.class)" になります。「魔法の魔法」の一部であるインターフェイスを自分で実装する必要はありません。

したがって、A->B、BA の例を次のように短縮できます。

public class CircularDepTest {

static class A {
    private final Provider<B> b;
    private String name = "A";

    @Inject
    public A(Provider<B> b) {
        this.b = b;
    }
}

static class B {

    private final Provider<A> a;
    private String name = "B";

    @Inject
    public B(Provider<A> a) {
        this.a = a;

    }
}

@Inject
A a;

@Inject
B b;

@Before
public void setUp() {
    Guice.createInjector().injectMembers(this);
}


@Test
public void testCircularInjection() throws Exception {
    assertEquals("A", a.name);
    assertEquals("B", a.b.get().name);
    assertEquals("B", b.name);
    assertEquals("A", b.a.get().name);
}}

私はこれを好みます。より読みやすく (コンストラクターが既に "B" のインスタンスを保持していると信じ込まないでください)、プロバイダーを自分で実装できるため、guice コンテキストの外で "手動で" 動作するからです (たとえばテスト用)。

于 2012-04-05T05:36:34.007 に答える
10

私はこの概念に慣れていませんが、これが私の理解です。

インターフェイスAB、および実装AiとがあるとしBiます。

Aiが に依存しており、 に依存しているB場合Bi、Guiceは( と呼ぶ)Aのプロキシ実装を作成できます。これは、将来のある時点で委譲先が与えられます。Guice は、への依存性のためにそれを提供し、インスタンス化を完了できるようにします。次に、がインスタンス化されているので、Guice は でインスタンス化できます。すると、は良いことになったので、Guice はに委譲するように指示します。AApAiApBiABiBiAiBiAiApAi

ABがインターフェースではなかった場合(そして、Aiとがあっただけの場合Bi)、これは単に不可能Apです。AiBi

コードでは次のようになります。

public interface A {
    void doA();
}

public interface B {
    void doB();
}

public class Ai implements A {

   private final B b;

   @Inject
   public Ai(B b) {
       this.b = b;
   }

   public void doA() {
       b.doB();
   }
}

public class Bi implements B {
   private final A a;

   @Inject
   public Bi(A a) {
       this.a = a;
   }

   public void doB() {
   }
}

Guice が作成するプロキシ クラスは次のようになります。

public class Ap implements A {
    private A delegate;
    void setDelegate(A a) {
        delegate = a;
    }

    public void doA() {
        delegate.doA();
    }
}

そして、この基本的なアイデアを使用してすべてを配線します。

Ap proxyA = new Ap();
B b = new B(proxyA);
A a = new A(b);
proxyA.setDelegate(a);

そして、インタフェースとなしAiで とのみがあった場合は、次のようになります。BiAB

public class Ap extends Ai {
    private Ai delegate;

    public Ap() {
       super(_); //a B is required here, but we can't give one!
    }
}

インターフェイスの背後に十分なクラスを投げるだけで、すべてうまくいくでしょうか?

コンストラクターでプロキシを操作する方法には厳しい制限があると思います。つまり、Guice が A のプロキシに実際の A を設定する前に B が A を呼び出そうとすると、RuntimeException が発生することが予想されます。

于 2012-04-04T16:02:49.023 に答える
2

これが@jan-galinskiの答えで、Scalaでやり直しました:

import javax.inject.Inject
import com.google.inject.{Guice, Injector, Provider}
import net.codingwell.scalaguice.InjectorExtensions._

/** Demonstrates the problem by failing with `Tried proxying CircularDep1$A to support a circular dependency, but it is not an interface.
  while locating CircularDep1$A for parameter 0 at CircularDep1$B.<init>(CircularDep.scala:10)
  while locating CircularDep1$B for parameter 0 at CircularDep1$A.<init>(CircularDep.scala:6)
  while locating CircularDep1$A` */
object CircularDep1 extends App {
  class A @Inject() (val b: B) {
    val name = "A"
  }

  class B @Inject() (val a: A) {
    val name = "B"
  }

  val injector: Injector = Guice.createInjector()
  val a: A = injector.instance[A]
  val b: B = injector.instance[B]

  assert("A" == a.name)
  assert("B" == a.b.name)
  assert("B" == b.name)
  assert("A" == b.a.name)
  println("This program won't run!")
}

/** This version solves the problem by using `Provider`s */
object CircularDep2 extends App {
  class A @Inject() (val b: Provider[B]) {
    val name = "A"
  }

  class B @Inject() (val a: Provider[A]) {
    val name = "B"
  }

  val injector: Injector = Guice.createInjector()
  val a: A = injector.instance[A]
  val b: B = injector.instance[B]

  assert("A" == a.name)
  assert("B" == a.b.get.name)
  assert("B" == b.name)
  assert("A" == b.a.get.name)
  println("Yes, this program works!")
}
于 2016-08-09T02:05:54.643 に答える