I need to create multiple instances of a spring bean (let's call it MainPrototypeBean), which I can do with the prototype scope. It depends on some other beans, and I want to create new instances of them each time the main bean is created. However, there is a shared dependency between some of the beans, let's call it SharedPrototypeBean. How do I inject the same instance of SharedPrototypeBean in each of the dependent beans, while also creating a new instance for each MainPrototypeBean?
I'm looking into implementing a custom scope, but I'm hoping to find a cleaner way. Making any of the beans singletons is not an option, as they need to be isolated between different instances of MainPrototypeBean.
Here's an example of what I'm trying to do:
@SpringBootApplication
public class DIDemo {
public static void main(String[]args){
ConfigurableApplicationContext context = SpringApplication.run(DIDemo.class, args);
context.getBean(MainPrototypeBean.class);
}
@Component @Scope("prototype") static class SharedPrototypeBean {}
@Component @Scope("prototype") static class FirstPrototypeBean {
@Autowired SharedPrototypeBean shared;
@PostConstruct public void init() {
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
@Component @Scope("prototype") static class SecondPrototypeBean {
@Autowired SharedPrototypeBean shared;
@PostConstruct public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
@Component @Scope("prototype") static class MainPrototypeBean {
@Autowired FirstPrototypeBean first;
@Autowired SecondPrototypeBean second;
}
}
And the output of executing it is:
FirstPrototypeBean.init() with shared DIDemo$SharedPrototypeBean@1b84f475
SecondPrototypeBean.init() with shared DIDemo$SharedPrototypeBean@539d019
You can use the FactoryBean for complex construction logic. Implement its abstract subclass AbstractFactoryBean for creating a MainPrototypeBean, and inject all three dependent beans into it. You can then wire them together in the createInstance method.
The FactoryBean implementation:
public class MainFactoryBean extends AbstractFactoryBean<MainPrototypeBean> implements FactoryBean<MainPrototypeBean> {
private FirstPrototypeBean firstPrototype;
private SecondPrototypeBean secondPrototpye;
private SharedPrototypeBean sharedPrototype;
public MainFactoryBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
this.firstPrototype = firstPrototype;
this.secondPrototpye = secondPrototype;
this.sharedPrototype = sharedPrototype;
}
@Override
protected MainPrototypeBean createInstance() throws Exception {
MainPrototypeBean mainPrototype = new MainPrototypeBean();
firstPrototype.setSharedPrototypeBean(sharedPrototype);
secondPrototpye.setSharedPrototypeBean(sharedPrototype);
mainPrototype.first = firstPrototype;
mainPrototype.second = secondPrototpye;
//call post construct methods on first and second prototype beans manually
firstPrototype.init();
secondPrototpye.init();
return mainPrototype;
}
@Override
public Class<?> getObjectType() {
return MainPrototypeBean.class;
}
}
Note: sharedPrototype is injected after the post-construct phase in the lifecycle of the first and second prototype. So, if you have post-construction logic in these beans that require the sharedPrototype, you need to manually call the init-method when creating the MainPrototypeBean.
Your annotation - configuration changes as as a consequence. The sharedPrototype attributes are no longer autowired (they are set inside FactoryBean), and MainPrototypeBean is not annotated anymore. Instead you need to create the MainFactoryBean.
@Configuration
public class JavaConfig {
//method name is the name refers to MainPrototypeBean, not to the factory
@Bean
@Scope("prototype")
public MainFactoryBean mainPrototypeBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
return new MainFactoryBean(firstPrototype, secondPrototype, sharedPrototype);
}
//Annotations are not needed anymore
static class MainPrototypeBean {
FirstPrototypeBean first;
SecondPrototypeBean second;
}
@Component
@Scope("prototype")
static class SharedPrototypeBean {
}
@Component
@Scope("prototype")
static class FirstPrototypeBean {
private SharedPrototypeBean shared;
//no autowiring required
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
@PostConstruct
public void init() {//reference to shared will be null in post construction phase
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
@Component
@Scope("prototype")
static class SecondPrototypeBean {
private SharedPrototypeBean shared;
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
@PostConstruct
public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
}
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