How do I instantiate a Spring Bean that has some @Autowired
beans within it? The caveat is that the instance bean type must be discovered dynamically at runtime (whereas the @Autowired
bean can still be singleton).
Example:
Interface
public interface Client {
public void process(String s);
}
ClientA.class
@Component
@Scope("prototype")
public class ClientA implements Client {
@Autowired
MyHelperService svc;
public void process(String s) {...}
}
ClientB.class
@Component
@Scope("prototype")
public class ClientB implements Client {
@Autowired
MyHelperService svc;
public void process(String s) {...}
}
ClientFactory.class
@Component
public class ClientFactory {
public Client createClient(String type) {
.. create an instance of ClientA or ClientB ..
}
}
MyService.class
@Service
public class MyService {
@Autowired
ClientFactory factory;
Client client;
public void processList(List<String> requests) {
for(String s: requests) {
client = factory.createClient(s);
client.process(s);
}
}
}
Though this sample code is for illustration purposes here, the pattern applies to our needs. More specifically, we are multi-threading our app by creating Callable<?>
tasks and submitting them to an ExecutorService
with parallel threads (and it's those tasks which each need their own instance of Client
whose lifespan should end after we call it's process
method).
The client instances use a singleton shared MyHelperService
service. Since that should be @Autowired
, our factory can't simply construct the clients like new ClientA()
or new ClientB()
.
CodePudding user response:
So you should know that adding a @Bean annotation on method, does not create a bean it just creates a bean definition, and only after calling this method spring will create a bean for you.
You can use applicationContext
to get bean defined with prototype
scope wherever you want in your application and your ClientFactory
is a good place to do that
public class ClientFactory {
private ApplicationContext applicationContext;
public ClientFactory(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public Client createInstance(String request) {
if (request.equals("A")) {
return applicationContext.getBean("clientA", Client.class);
}
return applicationContext.getBean("clientB", Client.class);
}
}
And your bean configuration will look like this
@Configuration
public class AppConfiguration {
@Bean
@Scope("prototype")
public Client clientA(){
return new ClientA();
}
@Bean
@Scope("prototype")
public Client clientB(){
return new ClientB();
}
@Bean
public ClientFactory clientFactory(ApplicationContext applicationContext){
return new ClientFactory(applicationContext);
}
}
Then you just need to inject your ClientFactory
and create instances of the Client
beans with clientFactory.createInstance("A");