The road so far….

July 14, 2010

Implement your factory in Spring XML

Filed under: java — Tags: — Rahul Sharma @ 10:00 pm

In most of our applications we create few objects from factories. For each of these factories we often end up writing custom code rather some kind of common component that can be reused. But reuse is one aspect of programming that we should strive for, rather writing code every time.

Spring provides us quite a few options that can be employed for implementing factories. These factories can be implemented in spring  XML file. Then they can be asked for objects that are created by other spring beans. The objects returned can be tweaked using the “scope” modifier, so that every time a new or the same object can be returned.

In order to see what all mechanisms Spring provides I have created the following class hierarchy.

public class MyObject {
 void operate() {
 System.out.println("operating in service one");
 }
}

class OneMoreObject extends MyObject {
 void operate() {
 System.out.println("one more service in operation");
 }
}

There are MyObject and OneMoreObject entities, both of the same type MyObject. Objects of these entities will be created by a custom Factory, which we will define using options available in spring.

Option One: ServiceLocatorFactoryBean

We can use the ServiceLocatorFactory bean to implement a factory in the Spring XML provided there is an interface for the Factory Itself. Basically it uses the proxy mechanism to create an implementation of the provided interface.

So if there is an interface like MyObjectFactory, having methods like  create() returning the Object of a given type.

public interface MyObjectFactory {
 MyObject create();
 MyObject create(String id);
}

Now a factory can be implemented using this interface in the spring XML file in the following manner.

<bean id="myFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface" value="info.dependencyInjection.spring.factory.MyObjectFactory" />
 <property name="serviceMappings">
  <props>
   <prop key="defaultObject">defaultObject</prop>
   <prop key="myobject">myObject</prop>
  </props>
  </property>
 </bean>

<bean id="myObject" class="info.dependencyInjection.spring.factory.MyObject" scope="prototype" />
<bean id="defaultObject" class="info.dependencyInjection.spring.factory.OneMoreObject" scope="prototype" />

Here we have created an instance of ServiceLocatorFactoryBean. The bean will create a proxy to the interface that will return objects  depending on the string id passed. Here we have injected a properties object that contains mapping of a key to the look up bean names e.g. the defaultObject key has the mapping to the same beanName. when we ask the factory for an object, it asks the spring context to get the bean having a name, as determined by the mapping. We have also created the beans using the scope=”prototype” modifier, so that every time when our factory asks the context for a bean it will get a new instance.

This factory can only return objects using the create(string) method, it can not create objects using the create() method.  A call to the create() api will return and exception.

If we do not have multiple objects of the same type (the type that the factory returns) we can eliminate the service mappings and  the create(string) method, so our new definition looks like

public interface MyObjectFactory {
 MyObject create();
}
<!-- XML definitions  -->
<bean id="myFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
 <property name="serviceLocatorInterface" value="info.dependencyInjection.spring.factory.MyObjectFactory" />
</bean>

<bean id="myObject" class="info.dependencyInjection.spring.factory.MyObject" scope="prototype" />

Now this factory will return the myObject when the create() method is called. The XML will have just one bean of type MyObject. But the moment you put one more bean of the same type spring will complain about objects of same type being found and you need to provide service mappings for the same. When we do not supply the mapping the bean looks up the context for possible matching types for the return type of the method called i.e. of type MyObject when create is called(), so if it finds multiple objects os the same type it can not decide which on to return and then throws an error.

But a minor variation in  the properties mapping can make both the apis work fine. Basically when we use the create() api it first tries to check the mapping, if available, using the key as “”(empty string). If it does not find a match for the key, then it performs a lookup in the context for possible matching types. So now we can define the properties in the following manner, and then both the apis will work fine.

<bean id="myFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
 <property name="serviceLocatorInterface" value="info.dependencyInjection.spring.factory.MyObjectFactory" />
 <property name="serviceMappings">
  <props>
   <prop key="defaultObject">defaultObject</prop>
   <prop key="myobject">myObject</prop>
   <prop key="">myObject</prop>
  </props>
  </property>
 </bean>

A call to create() will return bean having name myObject while a call to create(string id) will return bean having a name that is found for the key id.

@Test
 public void testfactory() {
  BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring/factory/factory.xml");
  MyObjectFactory factory = (MyObjectFactory) beanFactory.getBean("myFactory");
  MyObject createObj = factory.create();
  assertNotNull(createObj);
  System.out.println(createObj);
  createObj = factory.create("myObject");
  assertNotNull(createObj);
  System.out.println(createObj);
 }

Option two: Method Injection

Spring also provides method injection that can be at times be employed to implement some simple factories, where we need to return just one object every time.  In order to use it we can create an abstract class, as method injection works for classes using cglib, with create() as abstract method.

public abstract class MiObjectFactory {
 abstract public MyObject create();
}
<!-- XML definitions -->
<bean id="mifactory" class="info.dependencyInjection.spring.factory.mi.MiObjectFactory">
  <lookup-method name="create" bean="myObject" />
</bean>
<bean id="myObject" class="info.dependencyInjection.spring.factory.MyObject" scope="prototype" />

Here again the scope modifier governs if a new  instance is returned or not.

@Test
public void testMifactory() {
 BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring/factory/factory.xml");
 MiObjectFactory factory = (MiObjectFactory) beanFactory.getBean("mifactory");
 MyObject createObj = factory.create();
 assertNotNull(createObj);
 System.out.println(createObj);
 }
Advertisements

1 Comment »

  1. This post was very useful for me. Thanks mate!

    Comment by Ari (@aritz86) — February 20, 2013 @ 10:19 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: