Guice proxy to support circular dependency

In my code, the following error appears on startup:

Tried proxying com.bar.Foo to support circular dependency, but this is not the interface.

How exactly does this proxy work? If I just throw enough classes for interfaces, will everything be okay?

(I know that circular dependencies are usually the smell of code, but I think that is normal in this case.)

+8
java dependency-injection guice
source share
3 answers

While the "introduce interface" approach is completely valid and may even be the best solution in some cases, in general, you can use a simpler solution: Providers.

For each class, "A" can be managed, and also offers a " Provider<A> ". This is an internal implementation of the javax.inject.Provider interface whose get() message will be " return injector.getInstance(A.class) ". You do not need to implement the interface yourself, its part of "magic magic".

Thus, you can shorten the example A-> B, BA to:

 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", abget().name); assertEquals("B", b.name); assertEquals("A", baget().name); }} 

I prefer this because its more readable (you are not being fooled to keep in mind that the constructor already has an instance of β€œB”), and since you could implement the Providers yourself, it would still work β€œmanually”, outside the guice context (for testing, for example).

+7
source share

I am new to this concept, but here is my understanding.

Say you have interfaces A and B , as well as implementations of Ai and Bi .

If Ai has a dependency on B , and Bi has a dependency on A , then Guice can create an implementation of the proxy server A (let's call it Ap ), which will be provided at some point in the future by Ai for delegation. Guice gives Ap to Bi for his dependence on A , allowing Bi to complete the instance. Then, since Bi was created, Guice can create an instance of Ai using Bi . Then, since Ai now good at doing, Guice tells Ap delegate Ai .

If A and B were not interfaces (and you only had Ai and Bi ), this would simply not be possible, because creating Ap would require the extension Ai , which Bi already requires.

Here's what it might look like with 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() { } } 

The proxy class that Guice does will look like this:

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

And all this will be connected with this basic idea:

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

And this is how it would be if you had only Ai and Bi , without interfaces A and B

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

If I just throw enough classes for interfaces, will everything be okay?

I would suggest that there are strict restrictions on how a proxy server can interact with a constructor. In other words, if B tries to call A before Guice has the opportunity to populate the proxy with real A, then I would expect a RuntimeException.

+6
source share

Here is @ jan-galinski's answer redone in 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" == abname) assert("B" == b.name) assert("A" == baname) 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" == abget.name) assert("B" == b.name) assert("A" == baget.name) println("Yes, this program works!") } 
+2
source share

All Articles