Question

Consider this simple example.

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

In this example instance A knows about instance B, and instance B knows about instance A.

My question is: how to instantiate instance A with Guice, i.e. how to make Guice take care of this complex circle dependencies?

Was it helpful?

Solution

To answer your first question "how to instantiate instance A with Guice": you can simply add @Inject to the constructor:

class A {
   private final B b;

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

This works because the API for creating A doesn't have a circular dependency. Guice will just use the A constructor any time it needs to create or inject an A object.

If your question is how to use Guice to create an object where the API for creating the object has a circular dependency, see this blog post by Misko Hevery (as mentioned in Yury's answer).

OTHER TIPS

Your example is not an issue at all, since you're constructing B directly. But if you want to both A and B to be created by Guice, one or both should be an interface. You can do:

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;
   }
   // ...
}

Even if AImpl and BImpl are scoped as singletons, Guice can handle this injection (by way of a proxy). This works in a simple case like this at any rate... I imagine there could be more complex circular dependencies that it couldn't handle. Anyway, eliminating circular dependencies would be preferable, of course.

The answer is that you should not use a dependency injection framework while you have circular dependences in your code.

So, you have to refactor you code beforehand. As far as I know, there are two solutions for tightly coupled classes: either merge two classes into one or introduce new class and move common logic into it (for detail look here)

I think that NamshubWriter's proposal isn't very guicy. I think that in Guice, a constructor should do exactly one thing: assign parameters into fields. If there's anything else you need to do, put it into a factory or a provider.

In this case, we'll want a provider for A. The provider could directly call new B(), but then we'd directly couple A to B, which is what we tried to avoid in the first place. So we indirect the creation of B over a factory, which guice can provide for us via assistedInject. This code runs and compiles fine, and completely decouples A and B.

In a realistic scenario, you'd need to hide A and B behind interfaces to take advantage of the separation.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top