Reputation: 789
I ran into an error while implementing assisted injection.
Assisted injection worked up until I introduced another class called Manager which relies on assisted Person
class. Manager wants to use Person (@Assited Address)
. The code breaks at the point of constructing injector graph. It does not go further.
Injector injector = Guice.createInjector(myModule);
Intuitively, I understand when object A is assisted then B (who depends on A) is in fact also implicitly assisted through A.
Pls note, I checked SO. I think someone like ColinD would definitely know the answer How to use Guice's AssistedInject? How to bind Assisted Injected class to interface?
Out of curiosity, are there good techniques/tools to spot Guice misconfiguration and ease learning curve? I turned on ProvisionListener and using graph library. That helps a bit.
public class Person implements PersonInterface {
private String name;
private Address address;
@Inject
public Person(String name, @Assisted Address address) {
this.name = name;
this.address = address;
}
}
public interface PersonInterface {
public String getName();
public Address getAddress();
}
public interface PersonFactory {
public PersonInterface create(Address address);
}
public class Address {
private final String address;
public Address(String address) {
super();
this.address = address;
}
}
public class Manager implements IManager {
private final Person person;
@Inject
public Manager(Person person) {
this.person=person;
}
...
}
configure() {
install(new FactoryModuleBuilder()
.implement(PersonInterface.class, Person.class)
.build(PersonFactory.class));
//
bind(IManager.class).to(Manager.class);
}
Actual error is
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) No implementation for ...assisted_inject.Address annotated with @com.google.inject.assistedinject.Assisted(value=) was bound.
while locating ....assisted_inject.Address annotated with @com.google.inject.assistedinject.Assisted(value=)
for parameter 2 at ....assisted_inject.Person.<init>(Person.java:13)
Upvotes: 1
Views: 5654
Reputation: 12021
When you put this binding into your module:
bind(IManager.class).to(Manager.class);
Guice will try to create a new instance of The Manager
class. It looks for either one (but only one) constructor annotated with @Inject
or a as a fallback zero-argument constructor that is not private. This is the constructor that Guice will use:
@Inject
public Manager(Person person) {
this.person=person;
}
Now following the same rule Guice will try to instantiate a Person
by using an appropriate constructor and it will get stuck here:
@Inject
public Person(String name, @Assisted Address address) {
this.name = name;
this.address = address;
}
It will give up when trying to instantiate the address because of the @Assisted
annotation. This annotation is a BindingAnnotation and Guice treats these specially - it tries to find explicit bindings for them and there are none. Read about binding annotations and you will understand why.
Since your manager is stateful and apparently manages a single person you may want to create a factory for these managers, e.g.:
public interface IManagerFactory {
public IManager getIManager(PersonInterface p);
}
Then you will have an IManager
, e.g.:
public interface IManager {
public String getPersonName();
}
And an implementation that uses assisted injection:
public class Manager implements IManager {
private final PersonInterface person;
@Inject
public Manager(@Assisted PersonInterface person) {
this.person = person;
}
@Override
public String getPersonName() {
return person.getName();
}
}
You can bind these in your module:
class MyModule extends AbstractModule {
protected void configure() {
install(new FactoryModuleBuilder()
.implement(PersonInterface.class, Person.class)
.build(PersonFactory.class));
install(new FactoryModuleBuilder()
.implement(IManager.class, Manager.class)
.build(IManagerFactory.class));
}
}
Inject the factories:
@Inject
PersonFactory pf;
@Inject
IManagerFactory manF;
And use them accordingly, e.g.:
public void testGuice() {
PersonInterface pi = pf.create(new Address("boh"));
IManager im = manF.getIManager(pi);
System.out.println(im.getPersonName());
}
Upvotes: 4