Injecting a prototype bean into a singleton bean

by Prasanth Gullapalli

In Spring, most of the beans we work with are Singletons. If a singleton bean is wired with yet another singleton bean, there is absolutely no problem. But if it is wired with a bean which is of different scope, say prototype, how does it work? Here is the example:

	public class RequestProcessor {

		private RequestValidator validator;

		public void handleRequest(String requestId){
			validator.validate(requestId);
			// Process the request and update
		}

		public RequestValidator getValidator() {
			return validator;
		}

		public void setValidator(RequestValidator validator) {
			this.validator= validator;
		}

	}

	public class RequestValidator {

		private List<String> errorMessages = new ArrayList<String>();

		public RequestValidator() {
			System.out.println("Validator instance created!");
		}

		// Validates the request and populates error messages
		public void validate(String requestId){

		}

		public List<String> getErrorMessages() {
			return errorMessages;
		}

	}

And here is the spring configuration:

<bean id="requestProcessor" class="com.pramati.spring.RequestProcessor">
	<property name="validator" ref="validator"/>
</bean>

<bean id="validator" scope="prototype" class="com.pramati.spring.RequestValidator"/>

With this configuration, it is expected that when ever I fetch requestProcessor from application context, it will be wired with a new validator as we declared the validator bean is of prototype scope. But this does not happen.

When the application context gets initialized, it sees that requestProcessor is a singleton bean and initializes it to the context after wiring it with all the dependencies set. So from then onwards when we request context for requestProcessor, it return the same bean every time. To solve this issue, we have 2 approaches:

1. Lookup Method injection: For this, we have to declare the beans as follows:

	<bean id="requestProcessor" class="com.pramati.spring.RequestProcessor">
		<lookup-method name="getValidator" bean="validator"/>
	</bean>
	
	<bean id="validator" scope="prototype" class="com.pramati.spring.RequestValidator"/>
	

The Spring Framework implements method injection by using CGLIB library to generate dynamically a subclass that overrides the method. So for the method to be overridden, we have to define that method in the class and either provide a dummy implementation for it or make it abstract. Making a method abstract implies that class also has to be made abstract which will make it difficult to unit test. So providing a dummy implementation is a better choice.

Whenever we define a bean with lookup methods, Spring creates a subclass of the bean and overrides those methods which are marked as lookup-methods. And this subclassed bean gets registered into the context. The subclass delegates all the non-lookup methods to the original class. For the lookup methods, it overrides the implementation. So in our example, when getValidator() is called, it returns a new validator instance.

We can roughly imagine our new subclass(registered in container) like this:

requestProcessor = new RequestProcessor(){
	public RequestValidator getValidator(){
		return context.getBean("validator");
	}
};

We could have directly fetched the bean from application context in RequestProcessor itself. But this would mean that the class is directly coupled to Spring framework. To do this in a cleaner way, we can use lookup injection. This puts all the spring related stuff at one place.

2. Scoped Proxies: This can be implemented as:

	<bean id="requestProcessor" class="com.pramati.spring.RequestProcessor">
		<property name="validator" ref="validator"/>
	</bean>
	
	<bean id="validator" scope="prototype" class="com.pramati.spring.RequestValidator">
		<!-- This instructs the container to proxy the current bean-->
		<aop:scoped-proxy/>
	</bean>
	

Remember, in case of look up method injection, proxy is created for singleton bean. But in case of scoped proxies, proxy is created for prototype bean and wired into the singleton bean during the process of registering the singleton bean in the context. The proxy thus created understands the scope and returns instances based on the requirements of the scope. So in our case, requestProcessor holds a reference to proxy in place of validator.

And in case of lookup method injection, when requestProcessor gets loaded into the context, validator will not be initialized at all. And when we call the look up method, it returns the prototype bean. But instead of calling the method, if you try to directly access the prototype bean(assuming it is accessible), it gives a Nullpointer Exception as it didn’t get initialized(We are not wiring it using property tag of bean)

In case of this, we can also configure how a proxy can be created. It can be done in 2 ways
1. CGLIB library which directly subclasses the object. This is the default option of Spring. For this, we must have CGLIB library our class path.
2. Java Dynamic Proxies. For this to be activated, we have to call:

<aop:scoped-proxy proxy-target-class="false"/>

Here in this case, we don’t need any additional libraries in our class path. But the scoped bean must implement at least one interface and it has to be referred through the same interface at all places in order to get itself wired.

Few points to note:
1. Both method injection and scoped proxies work not only for prototype beans. This works more generic. Whenever a bean of different scope is injected into a singleton bean, we can use any of these techniques to ensure that we get a corresponding scope object.
2. Note that in the proxy, the method returning the prototype bean is overridden to return a new instance for every single call.
Suppose we want to display the error messages that we have got after validation:

	requestProcessor.getValidator().validate();
	for(String message: requestProcessor.getValidator().getErrorMessages()){
		logger.log(LogLevel.ERROR, message);
	}
	

This code seems to print the error messages we have got after validation process. But this will never print any error messages even if there are many validation failures. This happens because requestProcessor.getValidator() returns a new validator instance every time it is called. So for this to work, the code has to be modified as:

	RequestValidator validator = requestProcessor.getValidator();
	validator.validate();
	for(String message: validator.getErrorMessages()){
		logger.log(LogLevel.ERROR, message);
	}
	

This happens only in case of prototype beans but works perfectly in case of other non-singleton scopes(request, session, global-session).

Advertisements

15 thoughts on “Injecting a prototype bean into a singleton bean

  1. In Lookup Method approach I understand that I have to call getValidator() in RequestProcessor instead of accessing instance variable directly. And whenever I require a new instance I can call getValidator(). To avoid creating new instances we can call getValidator() once and hold a reference to it.

    I am interested in Scoped Proxy approach as it involves subclassing/proxying only one class RequestValidator. But in Lookup method approach we have to proxy all RequestValidator referring classes.

    But I am little confused with Scoped Proxy approach.
    Can I access instance variable directly in this approach?
    When does proxy returns the RequestValidator instance?
    When scope is ‘prototype’ how can I control creating new instance with this approach?

    • Yes. You can directly access it as it is a proxy which understands how to return instances based on scope. When you call the validator, which actually is a proxy, it intercepts the request and returns you the new instance.
      You can control instance creation, by holding a reference to it and doing the subsequent operations on the reference holded, like the way described in #2 in ‘Points to Note’

    • Yes, you can. Injecting Singleton bean into Prototype bean doesn’t have this issue as anyway the dependency is declared as Singleton and thus the same bean instance will be injected into each Prototype bean instances, as expected.

      Prototype bean (always new bean) —> Singleton bean (only one per context) = DOES WORK AS EXPECTED
      Singleton bean —> Prototype bean = WORKS AS EXPECTED

  2. Hi,
    As per spring documentation
    “You do not need to use the in conjunction with beans that are scoped as singletons or prototypes. ”
    so will the approach mentioned for the prototype bean injection into singleton bean will work ?

    • It will work, but documentation is also correct. What will happen is that a new object will be created on each prototype bean call. Perhaps that is not what was intended but will work fine if your prototype bean is stateless. For request and session scoped beans it will always work fine (they will be always retrieved from HTTP request or session but that will be the same object beneath the proxy within given scope). I would prefer the lookup method approach as it gives a chance to set the bean into temporary local variable and that would be the only option if your prototype bean is stateful e.g. if you would need to call
      prototypeBean.init();
      prototypeBean.doSomething();
      prototypeBean.done();

    • i gone through post..m not getting it..m not that much experienced …could u please say in simple sentence ,if prototype bean injected to singleton ,what will happen,how many object generate for prototype bean in this case and what is the best solution.

      • If prototype bean is injected to singleton, this singleton will hold a reference to only one single instance of prototype bean. Basically it will turn that prototype bean into singleton as well. The article above describes approaches about how to get a new instance of prototype bean, as it is intended.

  3. RequestValidator validator = requestProcessor.getValidator();
    validator.validate();
    for(String message: validator.getErrorMessages()){
    logger.log(LogLevel.ERROR, message);
    }

    In this code if validator is returned as proxied object, then both .validate() and .getErrorMessages() method will create a new instance of RequestValidator class… But if it is not proxied object – all works like described in the topic. Do you know the reason?..

  4. Pingback: inject prototype into singleton(java configuration + annotatopn) « news-Knowlage FeeD

  5. A good post. One more option I would like to add, as when it comes to use prototype bean in a singleton bean, can we use use springs ObjectFactoryCreatingFactoryBean, and then inject OjbectFactory into a singleton bean and retrive the protottype bean from ObjectFactory. Object factory dispense prototype beans. Correct me if I am wrong

  6. hi,

    RequestValidator requestValidator = requestProcessor.getValidator(); is giving me new instance every time. in RequestValidator class I have created one variable as private String msg; and getter and setter method for it. when I try to set a value for msg by using requestValidator.setMsg(“hello”); it is creating new instance for it. and when I try getting the same value requestValidator.getMsg(); result is null because it is creating again new instance for it.

    will you plz explain me this, and how should i get the same value.

    thanks in advance.

  7. What if I used like below:

    ———————————–
    public class MainDemo {

    public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext(“spring.xml”);

    RequestProcessor processor = (RequestProcessor) context.getBean(“requestProcessor”);
    processor.handleRequest(“1212”);
    System.out.println(“————————“);
    processor.handleRequest(“1213”);
    }
    }

    Why I am getting output + error like below?
    Constructor:: RequestProcessor instance created!
    Request ID : 1212
    Exception in thread “main” java.lang.NullPointerException
    at com.injection.testing.RequestProcessor.handleRequest(RequestProcessor.java:12)
    at com.injection.testing.MainDemo.main(MainDemo.java:12)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s