spring-FactoryBean
FactoryBean 是一个将对象构造逻辑封装在一个类中的一种设计模式。它可以用于构造复杂的对象,因为可能有许多的依赖关系,或者构造逻辑本身高度依赖于配置时,可以使用.FactoryBean 可以用于帮助Spring 构造无法轻松构造的对象。
我们先来看下这个模式的功能:
public interface FactoryBean<T> {
/**
* 返回一个实例
*/
@Nullable
T getObject() throws Exception;
/**
* 返回即将创建的实例的类型
*/
@Nullable
Class<?> getObjectType();
/**
* 返回是否单例
*/
default boolean isSingleton() {
return true;
}
}
举个例子:
首先有两个类 Person 和 Car ,Person 依赖 Car
Person类:
public class Person {
private Car car;
private void setCar(Car car) {
this.car = car;
}
}
Car类:
public class Car {
}
Car 的 builder:
public class CarBuilder {
private String make;
private int year;
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public static CarBuilder car() {
return new CarBuilder();
}
public Car factory() {
return new Car();
}
}
然后有个 MyCarFactoryBean 实现 FactoryBean
public class MyCarFactoryBean implements FactoryBean<Car> {
private String make;
private int year;
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public Car getObject() throws Exception {
// 如果你的构造逻辑很简单的话,其实没必要使用FactoryBean
CarBuilder cb = CarBuilder.car();
if (year != 0) {
cb.setYear(this.year);
}
if (StringUtils.hasText(this.make)) {
cb.setMake(this.make)
}
return cb.factory();
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
}
Bean 的配置:
<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>
简单说明一下,这个功能是提供一个Person 类的bean,并依赖 Car 的bean,但是其中Car 的bean 是通过 MyCarFactoryBean 实现 FactoryBean 来的,因为我们可以在 MyCarFactoryBean 中自定义Car 的构建过程。在这个例子中,FactoryBean 的 getObject() 将会被spring 调用,而不是 FactoryBean 本身。如果你使用的是java 配置的方式,它需要你手动调用getObject()方法了,例如
// 和 xml 模式配置功能一样
@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 FactoryBeans具有任何其他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在XML配置中很有用,作为其局限性的解决方法。 它是在静态XML中注入“动作”的方法,但是在 java 配置方式中,显得就不是那么有用了。