I am trying to inject a prototype
bean in a singleton
bean such that every new call to a singleton bean method has a new instance of the prototype
Spring wires up your beans in a pretty straight forward way. I'm working in a large commercial application, and I inserted the following code snippets to verify the load order.
1) All of your singleton bean class structures are initially loaded by Spring (as long as Spring is aware of them via annotations and/or xml). This only ever happens once. You can test this by logging or printing in a static block:
static {
log.info("#### classNameHere loaded"); //or println if no log setup
}
2) Spring creates all singleton instances that it is aware of (but not prototypes! Prototype instances WILL be created IF they are referenced inside a singleton bean - there class structures are of course loaded first). You can test this by adding this method to each class:
@PostConstruct
public void methodHitAfterClassInstantiation() {
LOGGER.info("#### instance of classNameHere");
}
So in your example, the class structures of SingletonBean is loaded when Spring starts up. A new instance of SingletonBean is created. And because PrototypeBean is Autowired inside of SingletonBean, its class structure is loaded and an instance of it is created. Now, if there was another bean, say AnotherSingletonBean, with an Autowired PrototypeBean inside of it, then a DIFFERENT instance of PrototypeBean would be created (no need to load the class structure again). So there is only ever 1 SingletonBean, and inside of it is a PrototypeBean, which will always point to the same bean. Because of this, singletons should always be stateless, as all of your other beans that use a singleton will be pointing at the same object. But you CAN maintain state in a prototype bean, because wherever you create a new reference, you will be pointing at another bean object. http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes-prototype
From Spring documentation:
You do not need to use the
<aop:scoped-proxy/>
in conjunction with beans that are scoped as singletons or prototypes. If you try to create a scoped proxy for a singleton bean, the BeanCreationException is raised.
It seems the documentation has changed a bit for version 3.2 documentation where you can find this sentence:
You do not need to use the
<aop:scoped-proxy/>
in conjunction with beans that are scoped as singletons or prototypes.
It seems that its not expected you use a proxied prototype bean, as each time it is requested to the BeanFactory
it will create a new instance of it.
In order to have a kind of factory for your prototype bean you could use an ObjectFactory
as follows:
@Component
public class SingletonBean {
@Autowired
private ObjectFactory<PrototypeBean> prototypeFactory;
public void doSomething() {
PrototypeBean prototypeBean = prototypeFactory.getObject();
prototypeBean.setX(1);
prototypeBean.display();
}
}
and your prototype bean would be declared as follows:
@Component
@Scope(value="prototype")
public class PrototypeBean {
// ...
}
Since Spring 4.1 you can use annotation @Lookup
@Lookup
public PrototypeBean getPrototypeBean() {
return null;
}
Every time you will call method getPrototypeBean() - you will receive new prototype bean instance. Don't worry about empty method realization: Spring will override it for you.
Read more in official documentation.
Right way to achieve it - use lookup method injection and everywhere where you used beans use lookup method invocation (detailed answer)
Singleton bean is created only once so the prototype bean which is injected also will be created once at the instantiation of singleton bean.The same instance of prototype bean will be used for every request.
If new instance of prototype bean will be created for each request at runtime ,the below method Injection can be used
Example
public class Singleton {
private Prototype prototype;
public Singleton(Prototype prototype) {
this.prototype = prototype;
}
public void doSomething() {
prototype.foo();
}
public void doSomethingElse() {
prototype.bar();
}
}
public abstract class Singleton {
protected abstract Prototype createPrototype();
public void doSomething() {
createPrototype().foo();
}
public void doSomethingElse() {
createPrototype().bar();
}
}
<bean id="prototype" class="ch.frankel.blog.Prototype" scope="prototype" />
<bean id="singleton" class="sample.MySingleton">
<lookup-method name="createPrototype" bean="prototype" />
</bean>