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.
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 | |
---|---|
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 command
bean 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 | |
---|---|
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. |
You can use to make a bean to be available with prototype bean inside a singleton bean.
ReplyDeleteHere 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
applicationContext.xml
ReplyDelete