Sunday, January 1, 2023

How to Inject Prototype Scoped Bean into a Singleton Bean in Spring

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.

  1. Implementing the ApplicationContextAware interface
  2. Lookup method injection in Spring
  3. aop:scoped-proxy
  4. 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.

  1. Using CGLIB library, this is the default option.
  2. 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 ObjectFactory reqHandlerObjectFactory;
 
 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

  1. Injecting Inner Bean in Spring
  2. Lazy Initializing Spring Beans
  3. Bean Scopes in Spring With Examples
  4. Wiring collections in Spring
  5. Using Conditional Annotation in Spring Framework

You may also like-

  1. Spring XML Configuration Example
  2. Spring JdbcTemplate Insert, Update And Delete Example
  3. Difference Between component-scan And annotation-config in Spring
  4. How HashMap Works Internally in Java
  5. static Import in Java With Examples
  6. Interface Static Methods in Java
  7. Java Multithreading Interview Questions And Answers
  8. Method Reference in Java

5 comments:

  1. That's a nice post, other Spring related posts are also excellent!

    ReplyDelete
  2. nice job, thanks , please keep on posting ..

    ReplyDelete
  3. In the first examplea
    ClassPathXmlApplicationContext("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();

    ReplyDelete
    Replies
    1. RequestManager rm1= (RequestManager)ap.getBean("requestManager");
      This statement alone won't work as the bean definitions are already loaded so you will get the same instance. You can verify it.

      Delete