http://www.developer.com/http://www.developer.com/java/ent/define-factories-in-spring-xml-using-method-injection.html
Sometimes in Spring-based applications I need to define conditional beans. For example, suppose I need to define an image service such that depending on the image type provided it chooses an appropriate reader (i.e. if the image type is JPEG it will use JpegReader and if the image type is GIF it will use GifReader). To create such a service, I can create a factory that encapsulates the details of object creation and the service will return the required reader for the given reader type. In a factory, instances are created manually using the In this article, I demonstrate how to define factories in XML using method injection and ServiceLocatorFactoryBean. I use the image service as the demo application, an application in which all the beans are created and wired using a dependency injection container like Spring and a factory in which I manually create the beans. Specifically, I will create a SimpleImageFactory (see below), which depending on the image type will return the correct image reader. I have an ImageService, which will have a SimpleImageFactory injected into it. The factory can be injected into the service using a dependency injection container like Spring, or it can be used directly in the code. This is the easiest approach to implement the factory but it has a few limitations: To remove some of these limitations I can use method injection. A better approach could be to use the power of method injection in my factory. Method injection allows a container to inject methods instead of objects and provides dynamic sub-classing. In other words, method injection provides a mechanism by which you can inject objects in your factory methods. To make this approach more concrete, I will apply this to my existing solution of using a simple factory. ImageService will have an ImageReaderFactory (see below), which again is injected into the service using a dependency injection container like Spring. ImageReaderFactory is an abstract class with three abstract methods: The method does not have to be abstract, but even if you make it non-abstract, the dynamically created class will override the concrete method provided in the class. You need to define the following in your Spring context file: The method injection approach has an advantage over writing a simple factory: the beans are managed by the dependency injection container and you do not create the new instances in your code manually. The beans On the other hand, using method injection has its advantages as well: To remove these limitations, I use the power of Spring ServiceLocatorFactoryBean, which provides the best solution to implement such conditional factories. Using method injection along with a factory was a better approach than using a simple factory, but I still have to write all the conditional code (i.e. if the report type is GIF then give me GifReader and so on). A much better solution is to use ServiceLocatorFactoryBean, which is a FactoryBean implementation that takes an interface that must have one or more methods with the signature Using ServiceLocatorFactoryBean ,I just need to define a service locator interface called ReadersFactory, which has one method I defined a bean ServiceLocatorFactoryBean and provide it the serviceLocatorInterface, which in this case is ReadersFactory. After that, I injected a properties object that contains the mapping of With this approach, I can use the power of ServiceLocatorFactoryBean to define conditional factories in Spring XML without any of the usual method injection limitations. Shekhar Gulati -- Contributing Editor, Java -- is a Java consultant with over 5 years experience. He currently works with Xebia India, an Agile Software Development company. The opinions in this article and on his blog are his own and do not necessarily represent the opinions of his employer. His own blog is at and you can follow him on twitter here.
Define Factories in Spring XML Using Method Injection
April 4, 2011
new operator.import com.shekhar.image.ImageType;
public class SimpleImageReaderFactory implements ReadersFactory {
public ImageReader getReader(ImageType imageType) {
switch (imageType) {
case GIF:
return new GifReader();
case JPEG:
return new JpegReader();
default:
return new DefaultReader();
}
}
} public class ImageService implements Service {
private ReadersFactory factory;
public ImageService(ReadersFactory factory) {
this.factory = factory;
}
/**
* Reads image for {@link InputStream} for a given image type
*/
public Image readImage(InputStream in) {
ImageType imageType = getImageType(in);
ImageReader reader = factory.getReader(imageType);
Image image = reader.read(in);
return image;
}
private ImageType getImageType(InputStream in) {
return ImageType.GIF;
}
}
Using a Factory with Method Injection
public abstract class ImageReaderFactory implements ReadersFactory {
public ImageReader getReader(ImageType imageType) {
switch (imageType) {
case GIF:
return getGifReader();
case JPEG:
return getJpegReader();
default:
return getDefaultReader();
}
}
protected abstract ImageReader getGifReader();
protected abstract ImageReader getJpegReader();
protected abstract ImageReader getDefaultReader();
}getJpegReader(), getGifReader(), and getDefaultReader().The Spring Framework implements this method injection by dynamically generating a subclass and overriding the method, using bytecode generation via the CGLIB library. The method to be 'injected' must have the following signature.[abstract] theMethodName(no-arguments);<?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-2.5.xsd"
default-autowire="byName">
<bean id="serviceWithFactory" class="com.shekhar.service.ImageService">
<constructor-arg>
<ref bean="imageReaderFactory" />
</constructor-arg>
</bean>
<bean id="jpegReader" class="com.shekhar.reader.JpegReader" scope="prototype" />
<bean id="gifReader" class="com.shekhar.reader.GifReader" scope="prototype" />
<bean id="defaultReader" class="com.shekhar.reader.DefaultReader"
scope="prototype" />
<bean id="imageReaderFactory" class="com.shekhar.reader.ImageReaderFactory">
<lookup-method bean="jpegReader" name="getJpegReader" />
<lookup-method bean="gifReader" name="getGifReader" />
<lookup-method bean="defaultReader" name="getDefaultReader" />
</bean>
</beans>The Pro and Cons of Using Method Injection
gifReader, jpegReader, and defaultReader are prototype because they might be stateful.
cglib-nodep dependency (if you don't have it already) in your application.Using Spring ServiceLocatorFactoryBean
MyService getService() or MyService getService(String id). It creates a dynamic proxy, which implements the interface using standard Java classes , delegating to an underlying BeanFactory.getReader that takes a String argument as imageType.public interface ReadersFactory {
public ImageReader getReader(ImageType imageType);
}</code></pre>
<p>An XML context config file will look like this:</p>
<pre><code><?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-2.5.xsd"
default-autowire="byName">
<bean id="service" class="com.shekhar.service.ImageService">
<constructor-arg>
<ref bean="imageServiceFactory" />
</constructor-arg>
</bean>
<bean id="jpegReader" class="com.shekhar.reader.JpegReader" scope="prototype" />
<bean id="gifReader" class="com.shekhar.reader.GifReader" scope="prototype" />
<bean id="defaultReader" class="com.shekhar.reader.DefaultReader"
scope="prototype" />
<bean id="imageServiceFactory"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface" value="com.shekhar.reader.ReadersFactory"></property>
<property name="serviceMappings">
<props>
<prop key="JPEG">
jpegReader
</prop>
<prop key="GIF">
gifReader
</prop>
</props>
</property>
</bean><?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-2.5.xsd"
default-autowire="byName">
<bean id="service" class="com.shekhar.service.ImageService">
<constructor-arg>
<ref bean="imageServiceFactory" />
</constructor-arg>
</bean>
<bean id="jpegReader" class="com.shekhar.reader.JpegReader" scope="prototype" />
<bean id="gifReader" class="com.shekhar.reader.GifReader" scope="prototype" />
<bean id="defaultReader" class="com.shekhar.reader.DefaultReader"
scope="prototype" />
<bean id="imageServiceFactory"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface" value="com.shekhar.reader.ReadersFactory"></property>
<property name="serviceMappings">
<props>
<prop key="JPEG">
jpegReader
</prop>
<prop key="GIF">
gifReader
</prop>
</props>
</property>
</bean>
</beans>
</beans>key (to the look up the bean name for example). When ReadersFactory receives an argument with ImageType as "GIF", the gifReader bean will be returned.About the Author