Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a Diamond Object Graph in Guice (without Singleton)

Tags:

java

guice

I am using Guice for Dependency Injection and want to create an object graph that looks like this:

   d1        d2
  /  \      /  \
b1    c1  b2    c2
  \  /      \  /
   a1        a2
  • a1 and a2 denote instance of class A etc.
  • class A depends on classes B and C
  • classes B and C each depend on class D
  • However, the instance of D used in B and C should be the same

So I want to create two instances of the A class, each with this structure. A use case could be D being some kind of data object (say, a DB connection) that is used by the two clients B and C, that both are used in a servlet A.

Minimal Java class definition:

public class A {
    @Inject A(B b, C c) {}
}

public class B {
    @Inject B(D d) {}
}

public class C {
    @Inject C(D d) {}
}

public class D {}

Now I can create two instances of A:

Injector injector = Guice.createInjector(new DiamondModule());
A a1 = injector.getInstance(A.class);
A a2 = injector.getInstance(A.class);

However, if I do this, I will get four different instances of D, which is not what I want. Notice that declaring D a singleton will not help, as then I will get only a single instance of D.

I have seen the following question, but the answer to that one doesn't work here, or at least I do not know how: Guice inject single instance into multiple objects without using @Singleton

Does anyone have an idea how to solve this? Or is this design somehow wrong? Or is this already an instance where I have to declare my own scope?


Solution

Meanwhile I noticed that this question is a duplicate of Dependency injection: Scoping by region (Guice, Spring, Whatever), and based on the anwers to that question I came up with a solution.

The basic idea is to create a new injector for every instance of the diamond, like this:

public class MainModule extends AbstractModule {
    @Override
    protected void configure() {
    }

    @Provides
    A createA() {
        return Guice.createInjector().getInstance(A.class);
    }
}

Now make D an singleton, which is scoped to the particular injector.

// class D is a singleton
@Singleton
public class D {}
like image 514
olenz Avatar asked Dec 22 '25 07:12

olenz


1 Answers

This is called the "robots leg" problem (creating similar graphs with slightly different instances) and is handled here.

class LegModule extends PrivateModule {
   private final Class<? extends Annotation> annotation;

   LegModule(Class<? extends Annotation> annotation) {
   this.annotation = annotation;
}

@Override protected void configure() {
   bind(Leg.class).annotatedWith(annotation).to(Leg.class);
   expose(Leg.class).annotatedWith(annotation);

   bindFoot();
}

abstract void bindFoot(); 
}
public static void main(String[] args) {
   Injector injector = Guice.createInjector(
      new LegModule(Left.class) {
        @Override void bindFoot() {
          bind(Foot.class).toInstance(new Foot("leftie"));
        }
      },
      new LegModule(Right.class) {
        @Override void bindFoot() {
          bind(Foot.class).toInstance(new Foot("righty"));
        }
      });
 }
like image 52
Jan Galinski Avatar answered Dec 23 '25 23:12

Jan Galinski