I'm trying to figure out how Guice works and is struggling to find examples/tutorials for the specific scenarios I have.
Lets say I have the following classes:
The problem I'm having is that ClassA does not have a no-arg constructor and also have its own dependencies. I'm not sure how to inject these type of dependencies using an Injector.
ClassA
public class ClassA {
private final int number;
private final ClassB classB;
@Inject
public ClassA(int number, ClassB classB) {
this.number = number;
this.classB = classB;
}
public String message() {
return "ClassA number: " + number + ", ClassB number: " + classB.getNumber();
}
}
ClassB
@Getter
public class ClassB {
private final int number;
@Inject
public ClassB(@Assisted int number) {
this.number = number;
}
}
ClassC
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
public class ClassC {
final private int number;
final private ClassA classA;
@Inject
public ClassC(@Assisted int number, ClassA classA) {
this.number = number;
this.classA = classA;
}
public String message() {
String message = classA.messsage();
return message + ", ClassC number: " + number;
}
}
I have a factory interfaces for all classes with a create(int number)
method. In the module configure method I have the following:
install(new FactoryModuleBuilder().build(ClassAFactory.class));
install(new FactoryModuleBuilder().build(ClassBFactory.class));
install(new FactoryModuleBuilder().build(ClassCFactory.class));
The following is where I'm totally lost, how do I mix this all together?
Injector injector = Guice.createInjector(new AppModule());
ClassBFactory bFactory = injector.getInstance(ClassBFactory.class);
ClassB b = bFactory.create(2);
ClassAFactory aFactory = injector.getInstance(ClassAFactory.class);
ClassA a = aFactory.create(1);
ClassCFactory cFactory = injector.getInstance(ClassCFactory.class);
ClassC c = cFactory.create(3);
System.out.println(c.message());
Output for above:
ClassA number: 3, ClassB number: 3, ClassC number: 3
Bonus for me, if its possible to use providers instead of factories for this scenario please can someone show me an example of how to resolve these dependencies in a provider?
This is actually a feature, believe it or not. It's creating corresponding instances with the same value.
ClassBFactory bFactory = injector.getInstance(ClassBFactory.class);
ClassB b = bFactory.create(2);
This creates a new ClassB
instance with number
of 2
.
ClassAFactory aFactory = injector.getInstance(ClassAFactory.class);
ClassA a = aFactory.create(1);
The constructor for ClassA
takes number
and an instance of ClassB
. However, the instance of ClassB
you've created above is not bound and isn't available to the Injector. Since ClassB
can be created using assisted injection, though, it's able to creates one. It does and injects that into the instance of ClassA
. In this case, both ClassA
and ClassB
instances have number
of 1
.
ClassCFactory cFactory = injector.getInstance(ClassCFactory.class);
ClassC c = cFactory.create(3);
As above, this creates new ClassB
and ClassA
instances for this new ClassC
, each with number
of 3
.
If you want to address this, you need to change your factories to take the right dependencies and make those dependencies @Assisted
.
ClassBFactory bFactory = injector.getInstance(ClassBFactory.class);
ClassB b = bFactory.create(2);
ClassAFactory aFactory = injector.getInstance(ClassAFactory.class);
ClassA a = aFactory.create(1, b); // <--
ClassCFactory cFactory = injector.getInstance(ClassCFactory.class);
ClassC c = cFactory.create(3, a); // <--
If you're only going to have one instance of each of these classes, this is better done in a Module
as @Provides
methods rather than using assisted injection and factories:
@Provides
ClassA provideClassA(ClassB b) {
return new ClassA(1, b);
}
@Provides
ClassB provideClassB() {
return new ClassB(2);
}
@Provides
ClassC provideClassC(ClassA a) {
return new ClassC(3, a);
}