13.1 Overview
We discussed Constructor and Setter based dependency injection in earlier chapters and in his chapter we will discuss Method injection. In Constructor and Setter based dependency Injection, Spring creates the beans and injects them using the constructor or setter method but with Method Injection approach dependencies are resolved using method look ups in which specified method of given bean is called to get the bean instance.
13.2 Why do we need Method Injection?
Method Injection should be used is when a Singleton bean has a dependency on Prototype bean.
We know that the Spring beans can be created with singleton or prototype scope.
- Singleton: Instantiate only one object
- Prototype: Instantiate a new object every time.
Spring container resolves the dependencies at instantiation time which means if any singleton bean has a dependency of any prototype bean, then a new object of prototype bean will be instantiated and injected in to singleton bean at the time of instantiation of Singleton bean.
With this, the same prototype bean will always supplied from singleton bean.
Confused? Consider a Singleton Bean “A” which has a dependency of non singleton (prototype) bean “B” .Container will create only one instance of bean “A” thus will have only one opportunity to inject the prototype bean “B” in it and every time you makes a call to get bean B from bean A , always the same bean will be returned.
Lets write an example to see the above concept working
Write a ProptotypeBean like below
public class PrototypeBean { private String message; public PrototypeBean() { System.out.println("Prototype Bean Instantiated !!"); } public void setMessage(String message){ this.message = message; } public String getMessage(){ return this.message; } }
Write a SingletonBean which has a dependency on PrototypeBean like below
public class SingletonBean { private PrototypeBean prototypeBean; public SingletonBean() { System.out.println("Singleton Bean Instantiated !!"); } public PrototypeBean getPrototypeBean() { return prototypeBean; } public void setPrototypeBean(PrototypeBean prototypeBean) { this.prototypeBean = prototypeBean; } }
Define beans.xml to configure SingletonBean and PrototypeBean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="prototypeBean" class="PrototypeBean" scope="prototype"> <property name="message" value="Test Message" /> </bean> <bean id="singletonBean" class="SingletonBean" > <property name="prototypeBean" ref="prototypeBean"/> </bean> </beans>
Create test program to test the behaviour
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestProgram { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SingletonBean singleton = (SingletonBean)context.getBean("singletonBean"); PrototypeBean prototypeBeanA = singleton.getPrototypeBean(); PrototypeBean prototypeBeanB = singleton.getPrototypeBean(); System.out.println(prototypeBeanA); System.out.println(prototypeBeanB); System.out.println("Is prototypeBeanA and prototypeBeanA same ? " + (prototypeBeanA==prototypeBeanB)); } }
Run the program and we can see that getPrototypeBean() returned same object both time
If we need to have a different prototype bean instance every time than we have got two approaches
- Method Injection
- Use of ApplicationContextAware interface
13.3 How to use Method Injection
We need to create an abstract method in Singleton class with return type as PrototypeBean .and Prototype bean class will declare one method which returns the current instance (this).
Spring provides below tag that can be used for method injection.
“<lookup method name=”prototype_bean” value=”method_name”/>
This tag specifies that dependency will be resolved by calling method of a bean and since the bean is of type prototype, we will get new instance on every call.
SingletonBean.java public abstract class SingletonBean { public SingletonBean() { System.out.println("Singleton Bean Instantiated !!"); } public abstract PrototypeBean getPrototypeBean(); } PrototypeBean.java public class PrototypeBean { private String message; public PrototypeBean() { System.out.println("Prototype Bean Instantiated !!"); } public void setMessage(String message){ this.message = message; } public String getMessage(){ return this.message; } public PrototypeBean getPrototypeBean() { return this; } }
Define entries of beans in beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="prototypeBean" class="PrototypeBean" scope="prototype"> <property name="message" value="Test Message" /> </bean> <bean id="singletonBean" class="SingletonBean" > <lookup-method bean="prototypeBean" name="getPrototypeBean"/> </bean> </beans>
Create test program to test the behaviour
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestProgram { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SingletonBean singleton = (SingletonBean)context.getBean("singletonBean"); PrototypeBean prototypeBeanA = singleton.getPrototypeBean(); PrototypeBean prototypeBeanB = singleton.getPrototypeBean(); System.out.println(prototypeBeanA); System.out.println(prototypeBeanB); System.out.println("Is prototypeBeanA and prototypeBeanA same ? " + (prototypeBeanA==prototypeBeanB)); } }
Run the program and we can see that getPrototypeBean() returned different object both time
13.4 Using Application Context Aware approach
We discussed Application Context Aware interface in earlier chapters with which we can get the instance of application context in which the bean is configured.
Every time a call getBean() for a prototype object will returns a different bean so
- Implements Application Context Aware interface in a singleton bean
- In the getter method of prototype bean, we can explicitly make a call to getBean and returns the object.
PrototypeBean.java public class PrototypeBean { private String message; public PrototypeBean() { System.out.println("Prototype Bean Instantiated !!"); } public void setMessage(String message){ this.message = message; } public String getMessage(){ return this.message; } } SingletonBean.java import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class SingletonBean implements ApplicationContextAware { private PrototypeBean prototypeBean; private ApplicationContext applicationContext; public SingletonBean() { System.out.println("Singleton Bean Instantiated !!"); } public PrototypeBean getPrototypeBean() { prototypeBean= (PrototypeBean)applicationContext.getBean("prototypeBean"); return prototypeBean; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } }
Define entries of beans in beans.xml – In the singleton bean , we did not add the dependency of prototype bean, instead the dependency is resolved programatically
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="prototypeBean" class="PrototypeBean" scope="prototype"> <property name="message" value="Test Message" /> </bean> <bean id="singletonBean" class="SingletonBean" > </bean> </beans>
Test Program
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestProgram { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SingletonBean singleton = (SingletonBean)context.getBean("singletonBean"); PrototypeBean prototypeBeanA = singleton.getPrototypeBean(); PrototypeBean prototypeBeanB = singleton.getPrototypeBean(); System.out.println(prototypeBeanA); System.out.println(prototypeBeanB); System.out.println("Is prototypeBeanA and prototypeBeanA same ? " + (prototypeBeanA==prototypeBeanB)); } }
Run the program and we can see that getPrototypeBean() returned different object both time
13.5 Conclusion
Method Injection is the approach in which method name is configured which will be used to resolve the dependency. This approach is primarily used when a singleton bean has a dependency on a prototype bean and a new instance of prototype bean is required from a singleton bean every time.