http://www.developer.com/java/ent/define-factories-in-spring-xml-using-method-injection.html

Back to article

Define Factories in Spring XML Using Method Injection


April 4, 2011

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 new operator.

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.

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();
}
}
}

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.

 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;
}

}

This is the easiest approach to implement the factory but it has a few limitations:

  1. This approach leads to tight coupling, as I am manually creating the objects.
  2. This approach leads to boilerplate code of creating new instances and returning the beans based on the type.
  3. This approach does not use a dependency injection container like Spring to manage my beans. So, I am not making full use of Spring.

To remove some of these limitations I can use method injection.

Using a Factory with 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.

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();
}

ImageReaderFactory is an abstract class with three abstract methods: 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);

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:

<?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

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 gifReader, jpegReader, and defaultReader are prototype because they might be stateful.

On the other hand, using method injection has its advantages as well:

  1. You still have to write a factory class, and method injection relies on creating class proxies for which you have to add a cglib-nodep dependency (if you don't have it already) in your application.
  2. The method to be injected has to be a no argument method (i.e. you can't pass any parameters to it).

To remove these limitations, I use the power of Spring ServiceLocatorFactoryBean, which provides the best solution to implement such conditional factories.

Using Spring ServiceLocatorFactoryBean

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 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.

Using ServiceLocatorFactoryBean ,I just need to define a service locator interface called ReadersFactory, which has one method 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>

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 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.

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.

About the Author

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.

Sitemap | Contact Us