工厂方法与 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
案例:
JndiObjectFactoryBean
LocalSessionFactoryBean
SqlMapClientFactoryBean
ProxyFactoryBean
TransactionProxyFactoryBean