Wednesday, January 13, 2016

Singleton beans with prototype-bean dependencies

When you use singleton-scoped beans with dependencies on prototype beans, be aware that dependencies are resolved at instantiation time. Thus 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.
However, suppose you want the singleton-scoped bean to acquire a new instance of the prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs only once, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies. If you need a new instance of a prototype bean at runtime more than once, see Section 4.4.6, “Method injection”


4.4.6 Method injection

In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean, or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container only creates the singleton bean A once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it. The following is an example of this approach:
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

 private ApplicationContext applicationContext;

 public Object process(Map commandState) {
    // grab a new instance of the appropriate Command
    Command command = createCommand();
    // set the state on the (hopefully brand new) Command instance
    command.setState(commandState);
    return command.execute();
 }

 protected Command createCommand() {
    // notice the Spring API dependency!
    return this.applicationContext.getBean("command", Command.class);
 }

 public void setApplicationContext(ApplicationContext applicationContext)
                                                                  throws BeansException {
    this.applicationContext = applicationContext;
 }
}
The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, allows this use case to be handled in a clean fashion.

4.4.6.1 Lookup method injection

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 lookup typically involves a prototype bean as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to generate dynamically a subclass that overrides the method.
[Note]Note
For this dynamic subclassing to work, you must have the CGLIB jar(s) in your classpath. The class that the Spring container will subclass cannot be final, and the method to be overridden cannot be final either. Also, 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. Finally, objects that have been the target of method injection cannot be serialized.
Looking at the CommandManager class in the previous code snippet, you see that the Spring container will dynamically override the implementation of the createCommand() method. Your CommandManager class will not have any Spring dependencies, as can be seen in the reworked example:
package fiona.apple;

// no more Spring imports! 

public abstract class CommandManager {

 public Object process(Object commandState) {
    // grab a new instance of the appropriate Command interface
    Command command = createCommand();
    // set the state on the (hopefully brand new) Command instance
    command.setState(commandState);
    return command.execute();
 }

  // okay... but where is the implementation of this method?
 protected abstract Command createCommand();
}

In the client class containing the method to be injected (the CommandManager in this case), the method to be injected requires a signature of the following form:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);

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. For example:
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="command"/>
</bean>

The bean identified as commandManager calls its own method createCommand() whenever it needs a new instance of the command bean. You must be careful to deploy the commandbean as a prototype, if that is actually what is needed. If it is deployed as a singleton, the same instance of the command bean is returned each time.
[Tip]Tip
The interested reader may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be of use. The approach used in ServiceLocatorFactoryBean is similar to that of another utility class, ObjectFactoryCreatingFactoryBean, but it allows you to specify your own lookup interface as opposed to a Spring-specific lookup interface. Consult the JavaDocs for these classes as well as this blog entry for additional information ServiceLocatorFactoryBean.

3 comments:

  1. You can use to make a bean to be available with prototype bean inside a singleton bean.


    Here is the example.

    App.java

    package com.rajesh.learnjava.controller;

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    import com.rajesh.learnjava.bean.ControllerClass;

    public class App {
    public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext(
    "applicationcontext.xml");

    ControllerClass helloWorld = (ControllerClass) context.getBean("controller");

    System.out.println("1st service : " + helloWorld.getServiceClass().getStr());



    ControllerClass helloWorld2 = (ControllerClass) context.getBean("controller");

    System.out.println("1st service : " + helloWorld2.getServiceClass().getStr());



    System.out.println("########## Below is the service call with lookup-method option in spring and see the results ##########");


    ControllerClass controller3 = (ControllerClass) context.getBean("controller");

    System.out.println("2nd service : " + controller3.getServiceClasswithLookupMethod().getStr());

    ControllerClass controller4 = (ControllerClass) context.getBean("controller");

    System.out.println("2nd service : " + controller4.getServiceClasswithLookupMethod().getStr());


    System.out.println("########## Below is the service call with AOP-PROXIED option in spring and see the results ##########");


    ControllerClass controller5 = (ControllerClass) context.getBean("controller");

    System.out.println("2nd service : " + controller5.getPrototypeBean().getStr());

    ControllerClass controller6 = (ControllerClass) context.getBean("controller");

    System.out.println("2nd service : " + controller6.getPrototypeBean().getStr());

    }
    }


    ControllerClass.java

    package com.rajesh.learnjava.bean;

    public class ControllerClass {

    ServiceClass serviceClass;
    ServiceClasswithLookupMethod serviceClasswithLookupMethod;
    MyPrototypeBean prototypeBean;

    public ServiceClass getServiceClass() {
    return serviceClass;
    }

    public void setServiceClass(ServiceClass serviceClass) {
    this.serviceClass = serviceClass;
    }

    public ServiceClasswithLookupMethod getServiceClasswithLookupMethod() {
    return serviceClasswithLookupMethod;
    }

    public void setServiceClasswithLookupMethod(
    ServiceClasswithLookupMethod serviceClasswithLookupMethod) {
    this.serviceClasswithLookupMethod = serviceClasswithLookupMethod;
    }

    public MyPrototypeBean getPrototypeBean() {
    return prototypeBean;
    }

    public void setPrototypeBean(MyPrototypeBean prototypeBean) {
    this.prototypeBean = prototypeBean;
    }
    }


    ServiceClass.java

    package com.rajesh.learnjava.bean;

    public class ServiceClass {

    private int counter;

    public String getStr() {
    return " counter is:" + (++counter);
    }
    }


    ServiceClasswithLookupMethod.java

    package com.rajesh.learnjava.bean;

    public class ServiceClasswithLookupMethod {

    private int lookupCounter;

    public String getStr() {
    return " counter is:" + (++lookupCounter);
    }
    }



    MyPrototypeBean.java

    package com.rajesh.learnjava.bean;

    public class MyPrototypeBean {
    private int aopProxiedCounter;

    public String getStr() {
    return " counter is:" + (++aopProxiedCounter);
    }
    }


    applicationContext.xml




















    ReplyDelete