本文翻译自:https://spring.io/blog/2011/08/09/what-s-a-factorybean,水平有限,欢迎指正!
在这篇文章中,我将介绍Spring的 org.springframework.beans.factory.FactoryBean<T> 接口。该接口的定义是:
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<T> getObjectType();
boolean isSingleton();
}
FactoryBean
是一种把目标对象创建逻辑封装在一个类中的模式。
例如,
它可以被用于以可重用的
编码方式创建复杂对象图中的对象。
通常它被用来构建有许多依赖的复杂对象。它也可能用于解决那些构造逻辑不固定并且依赖配置来创建对象的问题。
FactoryBean
也对可以帮助Spring 创建那些它不能很容易创建的对象。例如,为了把引用注入到从JNDI获得的bean中,这个引用必须首先被获得。你可以使用
JndiFactoryBean
以统一的方式获取这个引用。你可以把从
FactoryBean
getObject()
方法得到的对象注入到任何其他对象的属性中。
假设你有一个
Person
类,其定义是这样的:
public class Person {
private Car car ;
private void setCar(Car car){ this.car = car; }
}
和一个
FactoryBean
,其定义是这样的:
public class MyCarFactoryBean implements FactoryBean<Car>{
private String make;
private int year ;
public void setMake(String m){ this.make =m ; }
public void setYear(int y){ this.year = y; }
public Car getObject(){
// wouldn't be a very useful FactoryBean
// if we could simply instantiate the object!
CarBuilder cb = CarBuilder.car();
if(year!=0) cb.setYear(this.year);
if(StringUtils.hasText(this.make)) cb.setMake( this.make );
return cb.factory();
}
public Class<Car> getObjectType() { return Car.class ; }
public boolean isSingleton() { return false; }
}
你可以使用一个假设的
CarFactoryBean
获取到一个
Car
实例,配置如下:
<bean class = "a.b.c.MyCarFactoryBean" id = "car">
<property name = "make" value ="Honda"/>
<property name = "year" value ="1984"/>
</bean>
<bean class = "a.b.c.Person" id = "josh">
<property name = "car" ref = "car"/>
</bean>
在这个例子中,
FactoryBean
的
getObject
方法的返回对象将会被创建,而不是
FactoryBean
本身。Spring 之所以把这个返回对象注入到目标属性中,是因为它会查看
FactoryBean
的
getObjectType()
返回值来确定工厂对象的类型,然后它会检查类型是否可以注入到注入位置。如果
FactoryBean
的
isSingleton()
方法返回true,
Spring 会缓存返回Bean,返回false就不会缓存
。
如果你正在使用Spring更新的(
在我看来也
更为优雅的)基于Java的配置,
你会发现这不像你期望的那样工作。
它仍可以工作,但你必须间接引用
FactoryBean
明确Java配置并像这样
调用
getObject():
// identical configuration in Java to the XML above
@Configuration
public class CarConfiguration {
@Bean
public MyCarFactoryBean carFactoryBean(){
MyCarFactoryBean cfb = new MyCarFactoryBean();
cfb.setMake("Honda");
cfb.setYear(1984);
return cfb;
}
@Bean
public Person aPerson(){
Person person = new Person();
person.setCar( carFactoryBean().getObject());
return person;
}
}
需要注意的是,从本质上讲,Spring 中所有的Bean
配置都会在
运行时同一个地方解析。你可以像上面那样基于Java配置定义
FactoryBean
,但是也可以在XML中使用工厂bean,就像你在XML中定义
FactoryBean
一样。
Spring中的
FactoryBean
拥有任何其他的Spring bean的所有其他特性,包括生命周期和Spring容器中所有Bean都会有的服务(如AOP)。
所以,如果你想在
FactoryBean
中属性被设置完成后,
getObject()
方法被调用之前执行对象创建逻辑,那么你可以告诉Spring容器给你
FactoryBean
回调。方法之一是实现
InitializingBean
接口。无论如何这个接口中的方法将会被调用。一个更能体现 POJO为中心思想的替代方法是用使用
@PostConstruct
注解。
在下面的例子中,当
make
和
year
属性被设置后,这个方法将被调用。
你可以使用这个回调在对象构造完成之前做完整性检查,而不是构造完成之后。
@PostConstruct
public void setup() throws Throwable {
// these methods throw an exception that
// will arrest construction if the assertions aren't met
Assert.notNull(this.make, "the 'make' must not be null") ;
Assert.isTrue(this.year > 0, "the 'year' must be a valid value");
}
看完这篇文章后,你需要明白的一个关键点是,
FactoryBean
不是在工厂对象本身本身,存在于Spring容器中,并享有生命周期和容器服务。返回的实例是transient的,Spring 并不关心
getObject()
返回
什么东西 ,也不会试图监听任何生命周期钩子或在其上执行任何其他操作。