Spring 3 Part 5: Factory Beans
This is the 5th part of the Spring 3 Series.
This is the solution to create instances which are not possible to crate via “new” operator. It is very important to note that, Spring creates transactional proxies using this method. Most of the time lazy initialization is the tactic of delaying the creation of object. Singleton Pattern is the way to lazy initialization, in turn Factory patterns (both Abstract Factory and Factory Method) are the way to inject an lazy instance to the Spring.
- Use the factory method to get an instance
- Use the lazy initialization to delay instantiation until first time request
- Store the instance for future delivery when request
- Inject the instance using Spring Factory Bean
Here the class diagram for the example I am going to explain
As shown in the class diagram, there are two concrete classes BlackDuck and ReadHeadDuck for the Duck (that is interface).
Here the BlackDuck
package com.blogspot.ojitha.spring3.part5.example.factoryExample;
import org.apache.log4j.Logger;
public class BlackDuck implements Duck {
private String name = "Black";
private int no=0;
Logger logger = Logger.getLogger(BlackDuck.class);
@Override
public int swim(String sound) {
logger.info("Hello! I am "+this.name+" ... "+sound);
return ++no;
}
}
Here the RedHeadDuck
package com.blogspot.ojitha.spring3.part5.example.factoryExample;
import org.apache.log4j.Logger;
public class RedHeadDuck implements Duck {
static final Logger logger = Logger.getLogger("RedHeadDuck.class");
private String name = "Red Head";
private int no=0;
/**
* {@inheritDoc}
*/
@Override
public int swim(String sound) {
logger.info("Hello! I am "+this.name+" ... "+sound);
return ++no;
}
}
DuckFactory is based on the Factory Method. It is capable of lazy initialization. DuckFactory class create either RedHeadDuck or BlackDuck based on the parameter passing in the getInstance(…) method as shown in the above class diagram.
package com.blogspot.ojitha.spring3.part5.example.factoryExample;
/**
* This is lazy init factory for to create {@link Duck}
* @author Ojitha
*
*/
public class DuckFactory {
//keep the duck instance
private static Duck duck;
//This is essential because Duck factory is not allowed to create directly
private DuckFactory(){}
/**
* Lazy instantiation for the
* key 1 - Red Head Duck
* key 2 - Black Duck
* @param key Pass either 1 or 2 for Red or Black ducks
* @return Red or Black duck according to the key
*/
public static synchronized Duck getInstance(int key) {
if (duck == null) {
switch (key) {
case 1:
duck = new RedHeadDuck();
break;
case 2:
duck = new BlackDuck();
break;
default:
throw new IllegalStateException("Pass either 1 or 2 for Red or Black duck");
}
}
return duck;
}
}
As shown in the above factory class, user can create either RedHeadDuck or BlackDuck.
All the basic requirements are implemented. Here the Spring configuration of factory bean;
package com.blogspot.ojitha.spring3.part5.example.factoryExample;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
public class DuckFactoryBean implements FactoryBean<Duck>, InitializingBean {
private Duck duck;
private int duckKey;
@Override
public void afterPropertiesSet() throws Exception {
this.duck = DuckFactory.getInstance(this.duckKey);
}
@Override
public Duck getObject() throws Exception {
return this.duck;
}
@Override
public Class<Duck> getObjectType() {
return Duck.class;
}
@Override
public boolean isSingleton() {
return true;
}
public int getDuckKey() {
return duckKey;
}
public void setDuckKey(int duckKey) {
this.duckKey = duckKey;
}
}
In the above code, the method getObject() defines the return type of the intended factory instance. This can be Class<?>, if the factory doesn’t define the return type until the runtime, but that approach is not support for Spring Auto-wiring. The method isSingleton() return true if you need the Spring factory bean to be singleton otherwise false There is getter and setter for the duckKey which can be either 1 or 2. The method afterPropertiesSet() returns the instance of the concrete class.
To make clear this application, the instance of the Duck has been injected to the Pond instance as shown in the following code
package com.blogspot.ojitha.spring3.part5.example.factoryExample;
public class Pond {
private Duck duck;
public Duck getDuck() {
return duck;
}
public void setDuck(Duck duck) {
this.duck = duck;
}
public void swimming(){
System.out.println("Duck swim "+duck.swim("Quack..")+" times");
System.out.println("Duck swim "+duck.swim("Quack..")+" times");
System.out.println("Duck swim "+duck.swim("Quack..")+" times");
System.out.println("Duck swim "+duck.swim("Quack..")+" times");
}
}
Here the Spring configuration 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-3.2.xsd">
<bean id="duckFactory" class="com.blogspot.ojitha.spring3.part5.example.factoryExample.DuckFactoryBean">
<property name="duckKey">
<!-- red head duck -->
<value>2</value>
</property>
</bean>
<bean id="pond" class="com.blogspot.ojitha.spring3.part5.example.factoryExample.Pond">
<property name="duck">
<ref local="duckFactory"/>
</property>
</bean>
</beans>
As shown in the above configuration in the bean “duckFactory”, you can change the duckKey value to create type of Duck either 1 from the RedHeadDuck or 2 from the BlackDuck concrete classes.
0 [main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/factory-config.xml]
200 [main] INFO org.springframework.context.support.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@332af08b: startup date [Sat Mar 02 22:58:15 EST 2013]; root of context hierarchy
236 [main] INFO org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7bb33228: defining beans [duckFactory,pond]; root of factory hierarchy
357 [main] INFO com.blogspot.ojitha.spring3.part5.example.factoryExample.BlackDuck - Hello! I am Black ... Quack..
Duck swim 1 times
357 [main] INFO com.blogspot.ojitha.spring3.part5.example.factoryExample.BlackDuck - Hello! I am Black ... Quack..
Duck swim 2 times
358 [main] INFO com.blogspot.ojitha.spring3.part5.example.factoryExample.BlackDuck - Hello! I am Black ... Quack..
Duck swim 3 times
358 [main] INFO com.blogspot.ojitha.spring3.part5.example.factoryExample.BlackDuck - Hello! I am Black ... Quack..
Duck swim 4 times
See the output to understand.
The above explained factory bean concept is based on the Adapter Pattern. However, if you need to directly use the Factory instead of factory bean then follow the following configuration:
<?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.2.xsd">
<bean id="duckFactory" class="com.blogspot.ojitha.spring3.part5.example.factoryExample.DuckFactory" factory-method="getInstance">
<constructor-arg value="1"/>
</bean>
<bean id="pond" class="com.blogspot.ojitha.spring3.part5.example.factoryExample.Pond">
<property name="duck">
<ref local="duckFactory"/>
</property>
</bean>
</beans>
Now “duckFactory” has changed to the direct factory which may be provided by the third-party.
For more information read the Instantiation with a static factory method.
you can download the source for this blog.
Comments
Post a Comment
commented your blog