Guice-Proxy zur Unterstützung der zirkulären Abhängigkeit

Beim Start wird in meinem Code der folgende Fehler angezeigt:

Versucht, com.bar.Foo als Proxy zu verwenden, um eine zirkuläre Abhängigkeit zu unterstützen, dies ist jedoch keine Schnittstelle.

Wie genau funktioniert dieses Proxying? Wenn ich nur genügend Klassen hinter Interfaces stecke, ist dann alles in Ordnung?

(Ich weiß, dass zirkuläre Abhängigkeiten normalerweise ein Codegeruch sind, aber ich denke, in diesem Fall ist es in Ordnung.)

Antworten auf die Frage(3)

die in Scala überarbeitet wurde:

<code>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!")
}
</code>

Injizieren einer Schnittstelle" absolut gültig ist und in einigen Fällen sogar die bessere Lösung sein kann, können Sie im Allgemeinen eine einfachere Lösung verwenden: Anbieter.

Für jede Klasse, die "A" Guice verwalten kann, bietet Guice auch eine "Provider<A>". Dies ist eine interne Implementierung der javax.inject.Provider-Schnittstelle, derenget() Nachricht wird "return injector.getInstance(A.class)". Sie müssen das Interface nicht selbst implementieren, es ist Teil der" Guice Magic ".

So können Sie das Beispiel A-> B, B-A verkürzen, um:

<code>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);
}}
</code>

Ich bevorzuge dies, da es besser lesbar ist (Sie können sich nicht täuschen, wenn der Konstruktor bereits eine Instanz von "B" enthält) und da Sie die Provider selbst implementieren könnten, würde es immer noch "von Hand" außerhalb des Guice-Kontexts funktionieren ( zum Testen zum Beispiel).

 specialtrevor19. Nov. 2013, 17:30
Ich bevorzuge auch diesen Ansatz. Dies bedeutet, dass Sie keine Schnittstellen erstellen müssen, wenn Sie sie sonst nicht benötigen würden. Es ist auch schneller / weniger problematisch, die Injection in einen Provider zu ändern, als Schnittstellen zu erstellen.
 Jan Zyka19. Jan. 2015, 10:30
Wie wäre es mit einer expliziten Abhängigkeit vonGuice in deinem app code? Ich habe immer gedacht, dass es schön ist, unabhängig vom DI-Framework zu bleiben. Bevor du dich vorstellstProvider zu deinem Code hättest du wechseln können, um zu sagenSpring das ist aber nicht mehr möglich.
 Jan Galinski19. Jan. 2015, 13:44
Ich spreche von javax.inject.Provider <T>, einer JSR330-Standardschnittstelle. Keine Guice-Abhängigkeit erforderlich.

Angenommen, Sie haben SchnittstellenA undBund ImplementierungenAi undBi.

ObAi hat eine Abhängigkeit vonB, undBi hat eine Abhängigkeit vonA, dann kann Guice eine Proxy-Implementierung von erstellenA (nennenAp), die irgendwann in der Zukunft eineAi delegieren an. Guice gibt dasAp zuBi für seine Abhängigkeit vonA, erlaubenBi Instantiierung zu beenden. Dann seitBi wurde instanziiert, Guice kann instanziierenAi mitBi. Dann seitAi ist jetzt gut zu tun, erzählt GuiceAp delegieren anAi.

ObA undB waren keine Schnittstellen (und Sie hatten geradeAi undBi) dies wäre einfach nicht möglich, weil erstellenAp würde erfordern, dass Sie verlängernAi, der schon einen brauchtBi.

So könnte es mit Code aussehen:

<code>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() {
   }
}
</code>

Die Proxy-Klasse, die Guice erstellt, sieht folgendermaßen aus:

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

    public void doA() {
        delegate.doA();
    }
}
</code>

Und alles würde mit dieser Grundidee verkabelt:

<code>Ap proxyA = new Ap();
B b = new B(proxyA);
A a = new A(b);
proxyA.setDelegate(a);
</code>

Und so wäre es, wenn du es nur hättestAi undBiohne SchnittstellenA undB.

<code>public class Ap extends Ai {
    private Ai delegate;

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

Wenn ich nur genügend Klassen hinter Interfaces stecke, ist dann alles in Ordnung?

Ich würde vermuten, dass es strenge Einschränkungen gibt, wie der Proxy im Konstruktor interagiert werden kann. Mit anderen Worten, wenn B versucht, A anzurufen, bevor Guice die Möglichkeit hatte, A's Proxy mit dem echten A zu füllen, würde ich eine RuntimeException erwarten.

Ihre Antwort auf die Frage