Method Injection using lookup-method property
Before start jumping into the topic, let me brief about the two important scopes in Spring and how that is working inside the spring container. Singleton scoped beans instances are created only once per container and they are re-used for the multiple requested. Prototype scoped beans instances are created for every request. It is ideal to consider that Singleton scope is suitable for defining the stateless beans and Prototype scope is suitable for stateful beans.
Lets jump into the topic, when we are injecting a bean into another bean (through Spring’s dependency injection) where both the beans are in the same scope would not have any problem. Lets consider this scenario, injecting a prototype bean into a singleton bean makes the situation difficult to handle because prototype bean is injected to the singleton bean only once at the time of container instantiation.
Spring provides method injection to solve the above problem. It is a another kind of injection used for dynamically overridding a class and its abstract methods to create instances every time the bean is injected. This tutorial highlights the key differences between normal @Autowired beans and injected with method injection. Lets look at this simple example to understand how spring lookup-method annotation used.
In the below example, we are using Prototype beans ResourceA and ResourceB which are injected into another Singleton bean RequestProcessor. ResourceB injected using the @Autowired and ResourceA injected using the method injection. In the main program, there is a loop to invoke the instances for the beans. The bean with method injection instantiated every time new request made.
Method Injection Example
ResourceA.java
package javabeat.net; public class ResourceA { String url ="http://localhost:8080"; public ResourceA(){ System.out.println("Resource A instance creation"); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
ResourceB.java
package javabeat.net; public class ResourceB { String url ="http://localhost:8081"; public ResourceB(){ System.out.println("Resource B instance creation"); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
RequestProcessor.java
package javabeat.net; import org.springframework.beans.factory.annotation.Autowired; public abstract class RequestProcessor { @Autowired ResourceB resourceB; public ResourceB getResourceB(){ return resourceB; } abstract ResourceA getResourceA(); }
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> <context:component-scan base-package="javabeat.net"></context:component-scan> <bean id="processor" class="org.RequestProcessor"> <lookup-method name="getResourceA" bean="resourceA"/> </bean> <bean id="resourceA" class="org.ResourceA" scope="prototype"/> <bean id="resourceB" class="org.ResourceB" scope="prototype"/> </beans>
SpringExample.java
package javabeat.net; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringExample { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("org/applicationContext.xml"); RequestProcessor processor = (RequestProcessor)applicationContext.getBean("processor"); for (int i=0;i<3;i++){ ResourceA resource = processor.getResourceA(); System.out.println(resource.getUrl()); } for (int i=0;i<3;i++){ ResourceB resource = processor.getResourceB(); System.out.println(resource.getUrl()); } } }
Output for the above program:
Resource B instance creation Resource A instance creation http://localhost:8080 Resource A instance creation http://localhost:8080 Resource A instance creation http://localhost:8080 http://localhost:8081 http://localhost:8081 http://localhost:8081
If you get this exception, that means that you are using the earlier version than Spring 3.2 and missing the libraries for CGLIB. Beginning Spring 3.2, all net.sf.cglib classes moved to org.springframework.cglib and inline them directly within the spring-core JAR. CGLIB is a Byte Code Generation Library is high level API to generate and transform Java byte code. It is a third part library now merged to the Spring framework itself.
Caused by: java.lang.NoClassDefFoundError: net/sf/cglib/proxy/CallbackFilter at org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.instantiateWithMethodInjection(CglibSubclassingInstantiationStrategy.java:71) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:75) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:948) ... 13 more Caused by: java.lang.ClassNotFoundException: net.sf.cglib.proxy.CallbackFilter at java.net.URLClassLoader$1.run(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) ... 16 more
Summary
This article helps you to understand how spring lookup-method annotation used for the method injection. It is one of the good feature to help when we inject the beans of different scopes. If you have any thoughts, please post it in the comments section.