How to inject dependencies in spring for objects created dynamically at runtime?
public class PlatformEventFactory {
public PlatformEvent createEvent(String eventType) {
if (eventType.equals("deployment_activity")) {
return new UdeployEvent();
}
return null;
}
}
I have a factory class that creates type objects PlatformEvent
based on eventType.
The UdeployEvent class has a dependency on private RedisTemplate<String, Object> template
which I want to add after the object is created UdeployEvent
.
@Component
public class UdeployEvent implements PlatformEvent {
private RedisTemplate<String, Object> template;
private UDeployMessage uDeployMessage;
private static final Logger logger = LoggerFactory.getLogger(UdeployEvent.class);
public UdeployEvent() {
uDeployMessage = new UDeployMessage();
}
/*public void sendNotification() {
}*/
public RedisTemplate<String, Object> getTemplate() {
return template;
}
@Autowired
public void setTemplate(RedisTemplate<String, Object> template) {
this.template = template;
System.out.println("Injection done");
}
}
When a new object is returned for UdeployEvent
, I get a null pointer exception for the template. I believe the reason for this is that it does not belong to the same bean that is created when spring boot. How can I inject dependencides for newly created objects at runtime.
source to share
You cannot create components manually. Let Spring. Use ApplicationContext
to get an instance of a component. All fields will be automatically entered:
@Component
public class PlatformEventFactory {
@Autowired
private ApplicationContext context;
public PlatformEvent createEvent(String eventType) {
if (eventType.equals("deployment_activity")) {
return context.getBean(UdeployEvent.class);
}
return null;
}
}
In order for Spring to create a new instance of a bean UdeployEvent
every time you request it, specify the scope of the bean as SCOPE_PROTOTYPE
:
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class UdeployEvent implements PlatformEvent {
private RedisTemplate<String, Object> template;
public RedisTemplate<String, Object> getTemplate() {
return template;
}
@Autowired
public void setTemplate(RedisTemplate<String, Object> template) {
this.template = template;
System.out.println("Injection done");
}
...
}
Now every time you call context.getBean(UdeployEvent.class)
Spring a new bean instance is created with fully initialized dependencies.
source to share
When you create objects manually, dependency injection is not performed on the created object and the field is null.
An easy way would be to use AutowireCapableBeanFactory
autowireBean()
Example:
@Component
public class PlatformEventFactory {
@Autowired
private AutowireCapableBeanFactory beanFactory;
public PlatformEvent createEvent(String eventType) {
if (eventType.equals("deployment_activity")) {
PlatformEvent platformEvent = new UdeployEvent();
beanFactory.autowireBean(platformEvent);
return platformEvent;
}
return null;
}
}
beanFactory.autowireBean(platformEvent)
should enter your fields and it should work fine.
There are more advanced solutions with @Configuration, but they produce a lot of boilerplate code and don't provide much in return.
Haven't seen a cleaner solution in Spring (like @AssistedInject in Guice).
Source: http://www.kubrynski.com/2013/09/injecting-spring-dependencies-into-non.html
source to share
You should annotate your bean as SCOPE_PROTOTYPE
@ ken-bekov pointed out.
To get a prototype bean instance, you can use injected (auto-notified) org.springframework.beans.factory.ObjectProvider<T>
rather than ApplicationContext
. The type parameter of this provider must be UdeployEvent
.
Alternatively, you can provide arguments for the class constructor or factory method in the method get
. One disadvantage of this approach is that the call get
will not be checked statically.
Another way to solve your problem is to use Google AutoFactory .
source to share