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
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?
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 {}
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"));
}
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With