I have a spring application and want to create a bean at runtime per request to inject it into another class, just like @Producer
for CDI.
My bean is just a simple POJO:
public class UserDetails {
private String name;
// getter / setter ...
public UserDetails(String name) {
this.name = name;
}
}
My producer class looks like this:
@Configuration
public class UserFactory {
@Bean
@Scope("request")
public UserDetails createUserDetails() {
// this method should be called on every request
String name = SecurityContextHolder.getContext()
.getAuthentication().getPrincipal(); // get some user details, just an example (I am aware of Principal)
// construct a complex user details object here
return new UserDetails(name)
}
}
And this is the class where the UserDetails
instance should be injected:
@RestController
@RequestMapping(value = "/api/something")
public class MyResource {
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<String> getSomething(UserDetails userDetails) {
// the userdetails should be injected here per request, some annotations missing?
// do something
}
}
The problem is that Spring complains at runtime about no default constructor (of course).
Failed to instantiate [UserDetails]: No default constructor found
But this is intended and I want to call my own factory to let it handle the Instantiation.
How can I achieve this? Why is UserFactory
never called?
This can be done without restarting the application at runtime when Loading and Removing bean in Spring Application. If the client code needs to register objects which are not managed by Spring container, then we will need to work with an instance of BeanDefinition. Here, We have used the following dependencies.
Request scope – Spring creates an instance of the bean class for every HTTP request. The instance exists only for that specific HTTP request. Session scope – Spring creates an instance and keeps the instance in the server's memory for the full HTTP session.
The prototype scope If the scope is set to prototype, the Spring IoC container creates a new bean instance of the object every time a request for that specific bean is made. As a rule, use the prototype scope for all state-full beans and the singleton scope for stateless beans.
Basically you aren't using your scoped proxy. You cannot inject a scoped proxy into a method, you have to inject it into your controller.
public List<String> getSomething(UserDetails userDetails) { ... }
This will lead to spring trying to create a new instance of UserDetails
through reflection, it will not inject your scoped bean. Hence it complains about the fact you need a default no-args constructor.
Instead what you should do is wire the dependency into your controller instead of the controller method.
@RestController
@RequestMapping(value = "/api/something")
public class MyResource {
@Autowired
private UserDetails userDetails;
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<String> getSomething() {
// the userdetails should be injected here per request, some annotations missing?
// do something
}
}
The idea is that the UserDetails
is a scoped proxy and when used will either use the already present object or create a new one based on the @Bean
method.
Additonally, the @Scope
annotation in the UserFactory
has to be modified as follows in order to work:
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
You're trying to inject a request scoped bean on a singleton bean.
You need to use a proxy for the UserDetails
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With