领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多在这篇文章中,我将介绍 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()
返回值以确定 factoried 对象的类型,然后它将检查该类型是否可以注入到注入位置。如果 FactoryBean
的 isSingleton()
方法返回 true,Spring 保留(但在实践中并不总是行使)缓存返回的 Bean 的权利。
如果您使用的是 Spring 的较新(并且在我看来更优雅)基于 Java 的配置,那么您会发现这并不像您期望的那样工作。它仍然可以工作,但您必须在 Java 配置中显式取消引用 FactoryBean
并自己调用 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 中定义的 FactoryBean
一样,在 XML 中使用工厂 Bean。
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
,而不是 factoried 对象本身,存在于 Spring 容器中并享受生命周期钩子和容器服务。返回的实例是瞬态的 - Spring 对您从 getObject()
返回的内容一无所知,并且不会尝试在其上执行任何生命周期钩子或任何其他操作。