It seems to me like you need a scope for each instance of RootObject
and all its dependencies.
In Guice you can create a custom scope, say @ObjectScoped
, like this:
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@ScopeAnnotation
public @interface ObjectScoped {}
Now just place RootObject
, A
, B
and D
into this scope:
@ObjectScoped
public class RootObject {
private A a;
private B b;
@Inject
public RootObject(A a, B b) {
this.a = a;
this.b = b;
}
public A getA() {
return a;
}
public B getB() {
return b;
}
}
@ObjectScoped
public class A {
private D d;
@Inject
public A(D d) {
this.d = d;
}
public D getD() {
return d;
}
}
// The same for B and D
Now each RootObject
has its own scope. You can implement this as a simple HashMap
:
public class ObjectScope {
private Map<Key<?>,Object> store = new HashMap<Key<?>,Object>();
@SuppressWarnings("unchecked")
public <T> T get(Key<T> key) {
return (T)store.get(key);
}
public <T> void set(Key<T> key, T instance) {
store.put(key, instance);
}
}
To integrate these scopes with Guice you will need a com.google.inject.Scope
-implementation which lets you switch the scopes and the corresponding wiring in your Module
.
public class GuiceObjectScope implements Scope {
// Make this a ThreadLocal for multithreading.
private ObjectScope current = null;
@Override
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
return new Provider<T>() {
@Override
public T get() {
// Lookup instance
T instance = current.get(key);
if (instance==null) {
// Create instance
instance = unscoped.get();
current.set(key, instance);
}
return instance;
}
};
}
public void enter(ObjectScope scope) {
current = scope;
}
public void leave() {
current = null;
}
}
public class ExampleModule extends AbstractModule {
private GuiceObjectScope objectScope = new GuiceObjectScope();
@Override
protected void configure() {
bindScope(ObjectScoped.class, objectScope);
// your bindings
}
public GuiceObjectScope getObjectScope() {
return objectScope;
}
}
Initialize your program like this:
ExampleModule module = new ExampleModule();
Injector injector = Guice.createInjector(module);
GuiceObjectScope objectScope = module.getObjectScope();
Create the first instance of RootObject
and its corresponding scope:
ObjectScope obj1 = new ObjectScope();
objectScope.enter(obj1);
RootObject rootObject1 = injector.getInstance(RootObject.class);
objectScope.leave();
Just switch the scope for a second group of objects:
ObjectScope obj2 = new ObjectScope();
objectScope.enter(obj2);
RootObject rootObject2 = injector.getInstance(RootObject.class);
objectScope.leave();
Test if your requirements are met:
assert rootObject1 != rootObject2;
assert rootObject1.getA() != rootObject2.getA();
assert rootObject1.getA().getD() == rootObject1.getB().getD();
assert rootObject1.getA().getD() != rootObject2.getB().getD();
To work with a group of objects just enter its scope and use the injector:
objectScope.enter(obj1);
B b1 = injector.getInstance(B.class);
objectScope.leave();
assert rootObject1.getB() == b1;