In this post we'll see different ways of injecting prototype scoped bean into singleton scoped bean in Spring so that new instance of prototype scoped bean is created every time.
Problem with injecting prototype scoped bean into singleton scoped bean
If we go by the definition of the singleton and prototype beans, it says-
- Singleton scope- Only one shared instance of a singleton bean is managed by the container, and all requests for beans with an id matching that bean definition result in that one specific bean instance being returned by the Spring container.
- Prototype Scope- Prototype scope for a bean results in the creation of a new bean instance every time a request for that specific bean is made.
Now we are confronted with a situation when we want to inject a prototype scoped bean into a singleton scoped bean in Spring. Since dependencies are resolved at instantiation time which means if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean.
The prototype instance is the sole instance that is ever supplied to the singleton scoped bean. You cannot dependency-inject a prototype-scoped bean (new instance of bean every time) into your singleton bean, because that dependency injection occurs only once, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies.
But that's not what you want, you have given a bean prototype scope with an intention that new instance of it should be created every time.
Probliem Statement
So let's see the problem first with some code. Let's say you have two classes RequestManager
and
RequestHandler
. RequestManager is configured as a singleton bean where as RequestHandler is
defined with a prototype scope.
<bean id="requestManager" class="org.netjs.prog.RequestManager"> <property name="requestHandler" ref="requestHandler" ></property> </bean> <bean id="requestHandler" class="org.netjs.prog.RequestHandler" scope="prototype"> </bean>
RequestManager Class
public class RequestManager { private RequestHandler requestHandler; public void handleRequest(){ requestHandler.handleRequest(); } public RequestHandler getRequestHandler() { return requestHandler; } public void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; } }
RequestHandler Class
public class RequestHandler { RequestHandler(){ System.out.println("In Request Handler Constructor"); } public void handleRequest(){ System.out.println("Handling request"); } }
Now if you run this code, using this class-
public class App { public static void main( String[] args ){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("appcontext.xml"); RequestManager bean = (RequestManager) context.getBean("requestManager"); // calling method three times bean.handleRequest(); bean.handleRequest(); bean.handleRequest(); context.close(); } }
Output
In Request Handler Constructor Handling request Handling request Handling request
Here, though method is called thrice, constructor is called only once which means RequestHandler instance is created only once.
Solutions for injecting prototype scoped bean into singleton scoped bean
In this post 4 solutions are given to ensure that a new instance is created every time when prototype scoped bean is injected into a singleton scoped bean.
- Implementing the ApplicationContextAware interface
- Lookup method injection in Spring
- aop:scoped-proxy
- Using ObjectFactory interface
1. Implementing the ApplicationContextAware interface
One solution is to implement the ApplicationContextAware interface and use the application context reference to get the bean with in the code. In that case RequestManager class will look like this. Note that according to the Spring docs this is not a good solution because the business code is aware of and coupled to the Spring Framework (look at the imports in the code). So if you want you can safely skip to the "Lookup method injection" solution.
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class RequestManager implements ApplicationContextAware{ private RequestHandler requestHandler; private ApplicationContext applicationContext; public void handleRequest(){ requestHandler = getRequestHandler(); requestHandler.handleRequest(); } // method to return new instance public RequestHandler getRequestHandler() { return applicationContext.getBean("requestHandler", RequestHandler.class); //return requestHandler; } /*public void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; }*/ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
And in XML configuration reference for RequestHandler will be removed from the RequestManager configuration.
<bean id="requestManager" class="org.netjs.prog.RequestManager"> </bean> <bean id="requestHandler" class="org.netjs.prog.RequestHandler" scope="prototype">
2. Lookup method injection to inject prototype scoped bean
Lookup method injection is another way to inject prototype scoped bean into a singleton bean in Spring. You can define a look up method in your bean definition using the <lookup-method> element.
Lookup method injection is the ability of the container to override methods on container managed beans, to return the lookup result for another named bean in the container. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to generate dynamically a subclass that overrides the method.
In this case the XML Configuration will look like this
<bean id="requestManager" class="org.netjs.prog.RequestManager"> <lookup-method name="getRequestHandler" bean="requestHandler"/> </bean> <bean id="requestHandler" class="org.netjs.prog.RequestHandler" scope="prototype" />
Note that the bean which is defined with the look up method will be dynamically subclassed by the Spring framework (using CGLIB library) and this subclass will override and provide implementation for the methods which are configured as look-up method.
The dynamically generated proxy will delegate all the non-lookup methods to the original class. For the lookup methods it will use the implementation it has provided.
Since look-up method has to be implemented so it has to be either defined as abstract method or you can provide some dummy implementation.
If the method is abstract, the dynamically-generated subclass implements the method. Otherwise, the dynamically-generated subclass overrides the concrete method defined in the original class.
If you are providing a dummy implementation then your RequestManager class will look like this-
public class RequestManager{ private RequestHandler requestHandler; public void handleRequest(){ requestHandler = getRequestHandler(); requestHandler.handleRequest(); } // dummy implementation, configured as look-up method public RequestHandler getRequestHandler() { return null; } }
In case you are defining the method as abstract then you will have to mark the class also as abstract class. It may create problem with in your whole implementation and also make the unit-testing difficult. Anyway in case you want it to be an abstract method then the RequestManager class will look like this -
public abstract class RequestManager{ private RequestHandler requestHandler; public void handleRequest(){ requestHandler = getRequestHandler(); requestHandler.handleRequest(); } public abstract RequestHandler getRequestHandler(); }
Now if you run it using this test class-
public class App { public static void main( String[] args ){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("appcontext.xml"); RequestManager bean = (RequestManager) context.getBean("requestManager"); bean.handleRequest(); bean.handleRequest(); bean.handleRequest(); context.close(); } }
Output
In Request Handler Constructor Handling request In Request Handler Constructor Handling request In Request Handler Constructor Handling request
Now you can see that three instances of RequestHandler are created for three separate calls.
Using @Lookup annotation
You can also use @Lookup
annotation, here are the updated classes to use annotations.
RequestManager class
import org.springframework.beans.factory.annotation.Lookup; import org.springframework.stereotype.Component; @Component public class RequestManager{ private RequestHandler requestHandler; public void handleRequest(){ requestHandler = getRequestHandler(); requestHandler.handleRequest(); } @Lookup public RequestHandler getRequestHandler() { return null; } }
RequestHandler class
@Component @Scope("prototype") public class RequestHandler { RequestHandler(){ System.out.println("In Request Handler Constructor"); } public void handleRequest(){ System.out.println("Handling request"); } }
XML Configuration
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.netjs.prog" /> </beans>
Some of the points to remember when using look-up method-
- For this dynamic subclassing to work, the class that the Spring bean container will subclass cannot be final, and the method to be overridden cannot be final either.
- Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
- Concrete methods are also necessary for component scanning which requires concrete classes to pick up.
3. Using aop:scoped-proxy
Third way to inject prototype scoped bean in a singleton bean is using aop scoped proxy. Though Spring docs say "You do not need to use the <aop:scoped-proxy/> in conjunction with beans that are scoped as singletons or prototypes." As it is more suitable to be used in the scenario when you are working with request, session and application scope and want to resolve the problem of how long do you want your bean to live.
As Spring docs say "you don't" not "you shouldn't" so we can anyway use it with singleton and prototype too.
When using aop scoped proxy the XML configuration will look like this -
<bean id="requestManager" class="org.netjs.prog.RequestManager"> <property name="requestHandler" ref="requestHandler"/> </bean> <bean id="requestHandler" class="org.netjs.prog.RequestHandler" scope="prototype"> <aop:scoped-proxy/> </bean>
Note that with look up method solution it was the singleton bean which was getting proxied but with aop scoped proxy it is the prototype bean which will be proxied.
So if we take our classes as example, the container will create a proxy object of the RequestHandler which can fetch the real RequestHandler class object from the defined scoping mechanism (prototype, request, session etc.)
The container injects this proxy object into the requestManager bean, which is unaware that this requestHandler reference is a proxy.
When a RequestManager instance invokes a method on the dependency-injected RequestHandler object, it actually is invoking a method on the proxy. The proxy then fetches the real RequestHandler object and delegates the method invocation onto the retrieved real RequestHandler object.
There are 2 ways to create proxy class using aop scoped proxy.
- Using CGLIB library, this is the default option.
- Using JDK interface-based proxies for such scoped beans, by specifying false for the value of the proxy-target-class attribute of the <aop:scoped-proxy/> <aop:scoped-proxy proxy-target-class="false" />
RequestManager Class when using aop scoped proxy
public class RequestManager{ private RequestHandler requestHandler; public void handleRequest(){ requestHandler.handleRequest(); } public RequestHandler getRequestHandler() { return requestHandler; } public void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; } }
RequestHandler Class
public class RequestHandler { RequestHandler(){ System.out.println("In Request Handler Constructor"); } public void handleRequest(){ System.out.println("Handling request"); } }
Output
In Request Handler Constructor Handling request In Request Handler Constructor Handling request In Request Handler Constructor Handling request
Same thing with annotations can be done by using the classes as given below.
RequestManager
@Component public class RequestManager{ @Autowired private RequestHandler requestHandler; public void handleRequest(){ requestHandler.handleRequest(); } }
RequestHandler
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; @Component @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) public class RequestHandler { RequestHandler(){ System.out.println("In Request Handler Constructor"); } public void handleRequest(){ System.out.println("Handling request"); } }
4. Using ObjectFactory interface
There is also an ObjectFactory interface that defines a factory which returns an Object instance (shared or independent) when invoked. In this case you'll need an independent object for the prototype scoped bean so you can encapsulate a generic factory which returns a new instance of some target object on each invocation.
RequestManager
import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class RequestManager{ @Autowired private ObjectFactoryreqHandlerObjectFactory; public void handleRequest(){ reqHandlerObjectFactory.getObject().handleRequest(); } }
That's all for this topic How to Inject Prototype Scoped Bean into a Singleton Bean in Spring. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Spring Tutorial Page
Related Topics
You may also like-
That's a nice post, other Spring related posts are also excellent!
ReplyDeletenice job, thanks , please keep on posting ..
ReplyDeleteRealy nice
ReplyDeleteIn the first examplea
ReplyDeleteClassPathXmlApplicationContext("appcontext.xml");
RequestManager bean = (RequestManager) context.getBean("requestManager");
// calling method three times
bean.handleRequest();
bean.handleRequest();
bean.handleRequest();
context.close();
}
It should be as below
ClassPathXmlApplicationContext ap= new ClassPathXmlApplicationContext("Prototype_in_Singleton_Program1.xml");
RequestManager rm= (RequestManager)ap.getBean("requestManager");
rm.handlerRequest();
RequestManager rm1= (RequestManager)ap.getBean("requestManager");
rm1.handlerRequest();
RequestManager rm1= (RequestManager)ap.getBean("requestManager");
DeleteThis statement alone won't work as the bean definitions are already loaded so you will get the same instance. You can verify it.