工厂方法与 FactoryBean
引子
直接依赖接口实现类的类Foo:
public class Foo
{
private BarInterface barInstance;
public Foo() {
// 我们应该避免这样做
// instance = new BarInterfaceImpl();
}
// ...
使用了工厂方法模式的Foo类可能定义:
public class Foo
{
private BarInterface barInterface;
public Foo()
{
// barInterface = BarInterfaceFactory.getInstance();
// 或者 // barInterface = new BarInterfaceFactory().getInstance();
}
...
此时接口BarInterface与BarInterfaceImpl解耦,BarInterface与 BarInterfaceFactory耦合,实现了替换BarInterface实现类只需要替换BarInterfaceFactory持有的实现类即可。
静态工厂方法(Static Factory Method)
不带参数的静态工厂方法:
静态工厂类:
public class StaticBarInterfaceFactory
{
public static BarInterface getInstance()
{
return new BarInterfaceImpl();
}
}
为Foo注入BarInterface的XML配置:
<bean id="foo" class="...Foo">
<property name="barInterface">
<ref bean="bar"/>
</property>
</bean>
<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>
其中,barInterface的注入方式为setter注入,为Foo类实例注入了BarInterfaceImpl的实例。且此处的id="bar"的bean指的是通过StaticBarInterfaceFactory的getInstance方法获得的BarInterfaceImpl实例。
注意
id="bar"的bean属性factory-method和class的作用
带参数的静态工厂方法:
静态方法带参数的静态工厂类:
public class StaticBarInterfaceFactory
{
public static BarInterface getInstance(Foobar foobar)
{
return new BarInterfaceImpl(foobar);
}
}
为Foo注入 BarInterface 的XML配置:
<bean id="foo" class="...Foo">
<property name="barInterface">
<ref bean="bar"/>
</property>
</bean>
<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance">
<constructor-arg>
<ref bean="foobar"/>
</constructor-arg>
</bean>
<bean id="foobar" class="...FooBar"/>
具体参数的使用在BarInterfaceImpl中实现,同上,此处的id="bar"的bean指的是通过 StaticBarInterfaceFactory 的 getInstance 方法获得的 BarInterfaceImpl 实例,构造参数foobar也是传递给BarInterfaceImp的。
注意
<constructor-arg>节点的作用
非静态工厂方法(Instance Factory Method)
非静态方法工厂类:
public class NonStaticBarInterfaceFactory
{
public BarInterface getInstance()
{
return new BarInterfaceImpl();
}
...
为Foo注入 BarInterface 的XML配置:
<bean id="foo" class="...Foo">
<property name="barInterface">
<ref bean="bar"/>
</property>
</bean>
<bean id="barFactory" class="...NonStaticBarInterfaceFactory"/>
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>
此处的工厂类作为普通bean注册到Spring容器中,而不是直接调用静态方法(需要实例化才能调用实例方法)。
如果非静态工厂方法调用时也需要提供参数的话,处理方式是与静态的工厂方法相似的,都可以通过<constructor-arg>来指定方法调用参数。
注意
factory-bean属性的作用。
FactoryBean
FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。它的实现类是专用于生产对象的bean。
当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这
个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候,就可以实现org.spring.framework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑代码。
FactoryBean定义:
public interface FactoryBean {
//返回产品类对象
Object getObject() throws Exception;
//返回产品类类型
Class getObjectType();
//返回产品类scope
boolean isSingleton();
}
每次得到的日期都是第二天工厂实现类:
import org.joda.time.DateTime;
import org.springframework.beans.factory.FactoryBean;
public class NextDayDateFactoryBean implements FactoryBean {
public Object getObject() throws Exception {
return new DateTime().plusDays(1);
}
public Class getObjectType() {
return DateTime.class;
}
public boolean isSingleton() {
return false;
}
}
将NextDayDateFactoryBean注册到Spring容器:
<bean id="nextDayDateDisplayer" class="...NextDayDateDisplayer">
<property name="dateOfNextDay">
<ref bean="nextDayDate"/>
</property>
</bean>
<bean id="nextDayDate" class="...NextDayDateFactoryBean">
</bean>
注意
id="nextDayDate"的bean没有使用factory-method属性就完成了依赖对象的生产,这里是FactoryBean接口的作用。
NextDayDateDisplayer类定义:
public class NextDayDateDisplayer
{
private DateTime dateOfNextDay;
// 相应的setter方法
// ...
}
由此,我们实现了NextDayDateDisplayer不依赖NextDayDateFactoryBean即可得到依赖的DateTime实例,即Spring容器通过工厂模式实现了类依赖注入。
使用&获取FactoryBean的实例演示:
//以下所有assert均通过
//获取工厂产品类DateTime
Object nextDayDate = container.getBean("nextDayDate");
assertTrue(nextDayDate instanceof DateTime);
//获取FactoryBean类的对象本身
Object factoryBean = container.getBean("&nextDayDate");
assertTrue(factoryBean instanceof FactoryBean);
assertTrue(factoryBean instanceof NextDayDateFactoryBean);
//通过上一步获得的FactoryBean获取工厂产品类DateTime的对象
Object factoryValue = ((FactoryBean)factoryBean).getObject();
assertTrue(factoryValue instanceof DateTime);
assertNotSame(nextDayDate, factoryValue);
assertEquals(((DateTime)nextDayDate).getDayOfYear(),((DateTime)factoryValue).getDayOfYear());
Spring容器使用FactoryBean案例:
JndiObjectFactoryBeanLocalSessionFactoryBeanSqlMapClientFactoryBeanProxyFactoryBeanTransactionProxyFactoryBean