Dagger2 can't inject field inside injected class - java

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.

Related

#Autowire within a DTO object - produces NullPointerException

On a spring boot 2.4.3 application with Java:
I am using a DTO to construct the JSON response from the domain model of the application. The DTO is just a plain java object.
I am trying to property inject a new class that I created for data transformation using the #Autowired but I get a nullPointerException on the runtime.
#Data
#NoArgsConstructor
public class FetchSupplierDTO {
private long id;
private String name;
private String description;
private String info;
private List<String> tags;
#Autowired
private TagTranslation tagTranslation;
public FetchSupplierDTO(SupplierEntity supplier) {
this.id = supplier.getId();
this.name = supplier.getDisplayName();
this.description = supplier.getGivenDescription();
this.info = supplier.getInfo();
if (supplier.getTags() != null) {
this.tags = tagTranslation.extractTagsFromEntity(supplier.getTags());
}
}
}
#Service
public class TagTranslation {
public List<String> extractTagsFromEntity(List<TagEntity> tagEntityList) {
List<String> tagStringList = new ArrayList<>();
tagEntityList.forEach(productTag -> { tagStringList.add(productTag.getTag()); });
return tagStringList;
}
}
First of all, looking at the current code I would design it so that the caller of the constructor is responsible for calling the autowired service. Then the DTO really stays a DTO and is not at the same time responsible for calling a service.
If there is really no way around calling a Spring component from inside a DTO then you will have to get the component manually. And call SpringBeanLocator.getBean(TagTranslation.class) to get that component and insert it in your field.
Spring holds a single instance of each component, on initialization it scans for autowired annotations within annotated classes (#Component, #Service) and initializes those fields. Once you call the constructor of such a class separately, it will not return the instance that is maintained by Spring, it will construct a new instance. Therefore it's autowired fields will be null.
public class SpringBeanLocator implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public final void setApplicationContext(ApplicationContext context) {
validate(getInternalContext());
setContext(context);
}
public static <T> T getBean(Class<T> type) {
context.getBean(type);
}
}

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.

Can we autowire the java bean in case of Spring MVC form tags?

I have seen many examples of Spring MVC form tags and each and every uses code like this:
class Student {
private String firstName;
private String lastName;
// necessary getters and setters (ommited here)
}
Now in the StudentController they create Student object as:
#Controller
class StudentController {
#RequestMapping("/showForm")
public String showForm(Model model) {
model.addAttribute("student", new Student());
return "show-form";
}
}
My question is that can we make the Student's scope prototype and make it a dependency of StudentController and make it autowired and let Spring inject it in the StudentController, something like this:
#Controller
class StudentController {
#Autowired
Student student;
#RequestMapping("/showForm")
public String showForm(Model model) {
model.addAttribute("student", student));
return "show-form";
}
}
Is it a valid point which i am making? If not please explain.
The sole purpose of the model is to carry data from the view to the corresponding handler in spring.Model is POJO which is plain old object which means it has only getters and setters .suppose in your case what you are trying to do is injecting the dependencies which is autowiring .
1.if the Model is singleton-scoped .the objects are created during the start of the application .i mean why do you want to create an object which has no purpose during the start of the application when its sole purpose is to use as a layer which couples the data.
2.if the Model is prototype scoped
#Component
#Scope("prototype")
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
why do want to write to unnecessary code while you can do it with a one liner
model.addAttribute("student", new Student());
Hope this helps .

Guice assisted inject, binding order

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

Categories