Consider the existence of a Foo some time in the app. Now Bar objects depend on a Foo. I want to create this bar based on this foo I already have, but Guice creates a new instance of Foo every time. I can have a lot of Foos, so I cannot @Singleton Foo.
I tried to use scopes for this purpose, but I don't know what I am doing wrong.
I know this can be solved with @AssistedInject but I do not want to take this solution because these associations are common in my context and the dependency is only one. Also, since I am working on creating a tool based on Guice, all I got is a Foo, and Bar.class and I want to create instances of Bar with the given Foo. I am not aware if there are any factories or so.
public class InjectionTest2 {
public static void main(String[] args) {
Module module = new AbstractModule() {
ReceiverScope receiverScope = new ReceiverScope();
@Override
protected void configure() {
bindScope(ReceiverScoped.class, receiverScope);
bind(Foo.class);
}
@Provides
@Singleton
public ReceiverScope scope() {
return receiverScope;
}
};
Injector injector = Guice.createInjector(module);
Foo foo = injector.getInstance(Foo.class); //assume a given foo
ReceiverScope scope = injector.getInstance(ReceiverScope.class);
scope.enter();
scope.seed(Key.get(Foo.class, Existing.class), foo);
Bar bar = injector.getInstance(Bar.class);//Create a Bar with this foo
scope.exit();
}
private static class Foo {
@Inject
public Foo() {
System.out.println("foo created" + toString());
}
}
@ReceiverScoped
private static class Bar {
@Inject
public Bar(@Existing Foo foo) {
System.out.println("bar created with foo" + foo.toString());
}
}
@Retention(RetentionPolicy.RUNTIME)
@BindingAnnotation
private static @interface Existing {
}
}
ReceiverScope is a class identical to Simple Scope given here.
Another way I can think of is if I manually iterate Bar's contructor parameters and for each parameter, I ask guice to give me an instance, but if it is a Foo, I give my value. But won't this give me a very hard time? I mean I will have to consider a lot of things, because in the constructor might be extra Qualifiers on parameters and stuff...
EDIT (after @kendavidson's answer):
This is true. I thought it makes my question more complicated, that's why I did not include it. Bar is an interface. Fields in Foo contain an annotation which has a Class<? extends Bar> barClass() method. For example:
@ContainsBars
private static class Foo {
@MakeBar(BlueBar.class)
private Something aSomething;
@MakeBar(BlackBar.class)
private SomethingElse aSomethingElse;
@Inject
public Foo() {
//create aSomething
//create aSomethingElse
}
}
Now, whenever Guice injects a @ContainsBars, I listen it via a TypeListener and I go to create the bars. I scan Foo.class and I get BlueBar.class & BlackBar.class.
The problem I am trying to solve is, how to create BlueBar when BlueBar has a dependency to Foo.class? Whenever this happens, I need to create the BlueBar with the Foo I was taken from the listener.
Reflecting constructor was one of my thoughts however it sounds a pain in the ... I guess, it will need to iterate each parameter of the @Inject constructor of BlueBar.class. If the parameter is of type Foo, I give my Foo. If it is a different type, ask the injector to give me an instance of that type. The thing is, how do I do this. When Guice injects, it takes in consideration possible binding annotations (Qualifiers) and generics. Doing that again, sounds pain.
In the other hand, these @ContainsBars annotated classes by 99% chance have no relation to each other.
Adding a method to Bar interface like setFoo(Foo f) will not work. It will have to be a generic. Therefore, method injection with a setter will be cast unsafe. As far as I am aware of, and assuming BlackBar implements Bar<Foo>, when I will Bar b = injector.getInstance(BlackBar.class), It will give me a Bar<?>. That means, I can't call the setter safely.
Field injection is interesting. Having something like this:
public class BlackBar implements Bar {
private MyDepedency dep;
@Existing
private Foo foo;
public BlackBar(Foo foo,MyDepedency dep) {
this.foo = foo;
this.dep = dep;
}
}
seems ok. Whenever, I get a BlackBar, I go find the @Existing field, and then set the value. I do not care for the value in constructor. Let's say:
Foo foo = ...
Bar bar = injector.getInstance(BlackBar.class);
setExistingFieldWithValueOf(foo,bar);
The problem here is that whenever I ask injector, it sees the Foo dependency, and it is creating a new Foo in order to inject. Then, I go change its value. This will cause an infinite loop since during Foo injection which is a @ContainsBar, the process will be repeated. Even if i manage to stop the infinite loop, still the creation of a new Foo that will never be used seems a bad overhead. If there is a way to inject with null value to foo in the constructor, let me know. I will use this.