Guice assisted inject, binding order - java

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)

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());
}

Related

Guice: Inject class with constructor with own parameters?

I have a problem with Guice. I would like to show this with an example. I want to pass the parameters of this constructor always dynamically, so e.g. new Test("bob", 765);. I also want some fields (like here SomeOtherObject) to be injected by Guice. How can I do this? Thanks in advance!
public class Test {
private String name;
private int id;
//Needs to be injected by Guice
#Inject
SomeOtherObject object;
public Test(String name, int id) {
this.name = name;
this.id = id;
}
}
Guice's AssistedInject feature is a good way to handle this.
Basically you define a factory to get Test objects with:
public interface TestFactory {
public Test create(String name, int id);
}
Augment the Test class's #Inject constructor with #Assisted annotation:
public class Test {
#Inject
public Test(SomeOtherObject object, #Assisted String name, #Assisted int id) {
this.object = object;
this.name = name;
this.id = id;
}
}
And then use FactoryModuleBuilder in your Module's configure:
install(new FactoryModuleBuilder().build(TestFactory.class));
And instead of constructing Tests directly, inject a TestFactory and use it to create Tests:
public class OtherThing {
#Inject
TestFactory factory;
public Test doStuff(Stirng name, int id) {
return factory.create(name, id);
}
}
Note: looking at the docs now, it appears that AutoFactory has been introduced as the preferred way to do this, and may be simpler.

How to properly publish DDD domain events with spring?

I am trying to implement domain driven design in my project.
Here is my base Aggregate class:
public abstract class UUIDAggregate {
private final DomainEventPublisher domainEventPublisher;
protected void publish(DomainEvent domainEvent) {
domainEventPublisher.publish(domainEvent);
}
}
Let's say we have UserAccount aggregate:
public class UserAccount extends UUIDAggregate {
private String email;
private String username;
private String password;
private String firstName;
private String lastName;
public void update() {
publish(new DomainEventImpl());
}
}
Here is my DomainEventPublisher:
public interface DomainEventPublisher {
void publish(DomainEvent event);
}
Here is DomainEventPublisherImpl:
#Component
public class DomainEventPublisherImpl implements DomainEventPublisher{
#Autowired
private ApplicationEventPublisher publisher;
public void publish(DomainEvent event){
publisher.publishEvent(event);
}
}
Now, this seems like a good idea, the domain is separated from implementation but this does not work. DomainEventPublisher cannot be Autowired because UUIDAggregate is not a #Component or #Bean . One solution would be to create DomainService and publish event there but that seems like leaking of domain to domain service and if I go that way, I am going to anemic model. Also what I can do is to pass DomainEventPublisher as a parameter to every aggregate but that also does not seems like a good idea.
One idea would be to have a factory for domain objects:
#Component
class UserAccountFactoryImpl implements UserAccountFactory {
#Autowired
private DomainEventPublisher publisher;
#Override
public UserAccount newUserAccount(String email, String username, ...) {
return new UserAccount(email, username, ..., publisher);
}
}
Then your code creating a domain object is "publisher-free":
UserAccount userAccount = factory.newUserAccount("john#example.com", ...);
Or you might slightly change the design of the event-publishing:
public abstract class UUIDAggregate {
private final List<DomainEvent> domainEvents = new ArrayList<>();
protected void publish(DomainEvent domainEvent) {
domainEvents.add(domainEvent);
}
public List<DomainEvent> domainEvents() {
return Collections.unmodifiableList(domainEvents);
}
}
#Component
class UserAccountServiceImpl implements UserAccountService {
#Autowired
private DomainEventPublisher publisher;
#Override
public void updateUserAccount(UserAccount userAccount) {
userAccount.update();
userAccount.domainEvents().forEach(publisher::publishEvent);
}
}
This is different from your proposal: the service publishes the events, but doesn't create then - the logic stays in the domain object.
Further, you can change your publisher to minimize the boiler-plate code:
public interface DomainEventPublisher {
void publish(UUIDAggregate aggregate);
}
Vaughn Vernon in his book IDDD just uses singleton like this:
DomainEventPublisher.instance().register(...);
DomainEventPublisher.instance().publish(...);
I know this approach doesn't use spring injection but it's much simplier than passing publisher to every aggregate and not that hard to test.

Dagger2 can't inject field inside injected class

I was trying to playaround with dagger2 but I got problems with field injection, and here is my code.
POJO classes :
// User.java
public class User {
private String firstName, lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
The class which I can't inject the user field.
//BackendService.java
public class BackendService {
#Inject
public User user; // Not injected -> Null
#Inject
public BackendService() {
}
}
User provider class
// UserModule.java
#Module
public class UserModule {
#Provides
#Singleton
User providesUser() {
return new User("AA","BB");
}
}
Backend provider class
// BackendServiceModule.java
#Module
public class BackendServiceModule {
#Provides
BackendService provideBackendService() {
return new BackendService();
}
}
And last but not least, the component
// ApplicationComponent.java
#Component(modules = {UserModule.class, BackendServiceModule.class})
public interface ApplicationComponent {
BackendService provideBackendService();
void inject(ConsumerMain consumerMain);
}
The problem is that in BackendService.java the field user doesn't get injected.
Injection is working properly on BackendService
Delete your #Provides BackendService method.
When you call new BackendService(), you're telling Dagger "don't worry about how to create a BackendService, I can do it for you". That also prevents Dagger from calling #Inject-annotated constructors and methods, or populating #Inject-annotated fields.
When you delete that method, Dagger will inspect BackendService to learn how to instantiate it itself, at which point it will see your #Inject constructor and field and use them to construct a BackendService when needed.

JSR-303 Validation on sub-class

I have the following class structure
public Abstract class Person {
private String fullName;
private Address address;
private Phone ;
}
class Staff extends Person{
private String staffId;
}
I want to apply validation using JSR-303 on class Staff whereby Staff address and phone cannot have the value of null. However, I have some other classes that are class of Person where I don't wish to have the validation to be applied.
One way to do this that I could think of is by refactor Person and push the fields 'address' and 'phone' to Staff, but this means refactoring a lot of other classes (and not to mention redundancy this shall cause), and hence something I want to avoid.
Update.
I have changed Staff class, as follows
public class Staff extends Person {
#NotNull
private String staffEmploymentId;
public String getStaffEmploymentId() {
return staffEmploymentId;
}
public void setStaffEmploymentId(String id) {
this.staffEmploymentId = id;
}
#Override
#NotNull
public void setPhones(List<Phone> phones) {
super.phones = phones;
}
#Override
#NotNull
public void setAddress(Address a) {
super.address = a;
}
#Override
#NotNull
public Address getAddress(){
return super.address;
}
}
However, I've got the following error.
javax.validation.ValidationException: Property setAddress does not follow javabean conventions.
I am using Apache BVal, as opposed to Hibernate Validator.
Annotate getters instead of fields using annotations from JSR-330.
You can override getters in Stuff and annotate them.

How to use PIcocontainer

I am using Picocontainer in a study project. I am having doubts about how to use it.
The following is the class I have :
public class DependencySupplier {
public static MutablePicoContainer pico;
static {
pico = new DefaultPicoContainer();
pico.registerComponentImplementation(CollectionDao.class, CollectionDaoImpl.class);
pico.registerComponentImplementation(ReadingDao.class, ReadingDaoImpl.class);
pico.registerComponentImplementation(CollectionDetails.class, CollectionDetailsImpl.class);
pico.registerComponentImplementation(Reading.class, ReadingImpl.class);
}
public static CollectionDao getCollectionDao() {
return (CollectionDao) pico.getComponentInstance(CollectionDao.class);
}
public static ReadingDao getReadingDao() {
return (ReadingDao) pico.getComponentInstance(ReadingDao.class);
}
}
My doubts are:
Is this the right way to use pico ?
The AddressImpl class is as follows:
public class AddressImpl implements Address {
private String address1;
private String address2;
private String address3;
private String address4;
public AddressImpl(String address1, String address2, String address3,
String address4) {
super();
this.address1 = address1;
this.address2 = address2;
}
public String getAddress1() {
return address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
public String getAddress3() {
return address3;
}
}
How can I instantiate the Address object with the above implementation as 'address1' and 'address2' has to be supplied by user and will be available on run time ?
Well, actually it's not a right way to use pico...
In most cases you should never directly lookup components from the pico context.
You need to register in pico your DAOs, services, other logic classes... They need to obtain referenced DAOs etc just declaring them as constructor arguments. Then in your bootstrap class that registers components you need to obtain from container your main logic class and call its method(s) (or use Startable lifecycle)
As for address instances, I'm not sure you need to instantiate them from the pico at all (cause I don't see ANY dependencies that container may fulfill for the Address, so what point?)
But still if you need to, you may register ready instances like pico.registerComponentInstance(new AddressImpl(...)) then you can inject all available instances with constructor argument Address[] addrs. There's another way, to instantiate several instances directly from the pico, but I think you just don't need it

Categories