领先一步
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() 返回值来确定工厂化对象的类型,然后检查该类型是否可以注入到注入点。Spring 保留——但在实践中并不总是行使——在 FactoryBean 的 isSingleton() 方法返回 true 时缓存返回的 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 中使用该工厂 bean,就像使用在 XML 中定义的 FactoryBean 一样。
Spring FactoryBeans 拥有任何其他 Spring bean 的所有其他特性,包括所有 bean 在 Spring 容器中享受的生命周期钩子和服务(如 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");
}
这里一个重要的收获是,生活在 Spring 容器中并享受生命周期钩子和容器服务的对象是 FactoryBean 本身,而不是被工厂化的对象。返回的实例是短暂的——Spring 对你从 getObject() 返回的任何内容一无所知,也不会尝试对其执行任何生命周期钩子或其他操作。