您的位置:首页 > 其它

三、通过 FactoryBean 来配置bean

2017-03-20 14:17 148 查看
一般情况下,Spring 通过反射机制利用 <bean> 的 class 属性指定实现类实例化 Bean ,在某些情况下,实例化 Bean 过程比较复杂,如果按照传统的方式,则需要在 <bean> 中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。 Spring 为此提供了一个 org.springframework.bean.factory.FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 Bean 的逻辑。

FactoryBean接口对于 Spring 框架来说占用重要的地位, Spring 自身就提供了 70 多个 FactoryBean 的实现。它们隐藏了实例化一些复杂 Bean 的细节,给上层应用带来了便利。从 Spring 3.0 开始, FactoryBean 开始支持泛型,即接口声明改为 FactoryBean<T> 的形式:

public interface FactoryBean<T> {

/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* <p>As with a {@link BeanFactory}, this allows support for both the
* Singleton and Prototype design pattern.
* <p>If this FactoryBean is not fully initialized yet at the time of
* the call (for example because it is involved in a circular reference),
* throw a corresponding {@link FactoryBeanNotInitializedException}.
* <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
* objects. The factory will consider this as normal value to be used; it
* will not throw a FactoryBeanNotInitializedException in this case anymore.
* FactoryBean implementations are encouraged to throw
* FactoryBeanNotInitializedException themselves now, as appropriate.
* @return an instance of the bean (can be {@code null})
* @throws Exception in case of creation errors
* @see FactoryBeanNotInitializedException
*/
T getObject() throws Exception;

/**
* Return the type of object that this FactoryBean creates,
* or {@code null} if not known in advance.
* <p>This allows one to check for specific types of beans without
* instantiating objects, for example on autowiring.
* <p>In the case of implementations that are creating a singleton object,
* this method should try to avoid singleton creation as far as possible;
* it should rather estimate the type in advance.
* For prototypes, returning a meaningful type here is advisable too.
* <p>This method can be called <i>before</i> this FactoryBean has
* been fully initialized. It must not rely on state created during
* initialization; of course, it can still use such state if available.
* <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
* {@code null} here. Therefore it is highly recommended to implement
* this method properly, using the current state of the FactoryBean.
* @return the type of object that this FactoryBean creates,
* or {@code null} if not known at the time of the call
* @see ListableBeanFactory#getBeansOfType
*/
Class<?> getObjectType();

/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
* <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
* the object returned from {@code getObject()} might get cached
* by the owning BeanFactory. Hence, do not return {@code true}
* unless the FactoryBean always exposes the same reference.
* <p>The singleton status of the FactoryBean itself will generally
* be provided by the owning BeanFactory; usually, it has to be
* defined as singleton there.
* <p><b>NOTE:</b> This method returning {@code false} does not
* necessarily indicate that returned objects are independent instances.
* An implementation of the extended {@link SmartFactoryBean} interface
* may explicitly indicate independent instances through its
* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
* implementations which do not implement this extended interface are
* simply assumed to always return independent instances if the
* {@code isSingleton()} implementation returns {@code false}.
* @return whether the exposed object is a singleton
* @see #getObject()
* @see SmartFactoryBean#isPrototype()
*/
boolean isSingleton();

}

在该接口中还定义了以下3 个方法:

T getObject():返回由 FactoryBean 创建的 Bean 实例,如果 isSingleton() 返回 true ,则该实例会放到 Spring 容器中单实例缓存池中;
boolean isSingleton():返回由 FactoryBean 创建的 Bean 实例的作用域是 singleton 还是 prototype ;
Class<T> getObjectType():返回 FactoryBean 创建的 Bean 类型。

当配置文件中<bean> 的 class 属性配置的实现类是 FactoryBean 时,通过 getBean() 方法返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 方法所返回的对象,相当于 FactoryBean#getObject() 代理了 getBean() 方法。

测试:

package com.xiya.spring.beans.factorybean;

/**
* Created by N3verL4nd on 2017/3/20.
*/
public class Car {
private String brand;
private int price;

public Car() {
}

public Car(String brand, int price) {
this.brand = brand;
this.price = price;
}

public String getBrand() {
return brand;
}

public void setBrand(String brand) {
this.brand = brand;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}

package com.xiya.spring.beans.factorybean;

import org.springframework.beans.factory.FactoryBean;

/**
* 通过 FactoryBean 来配置bean
* 需要实现 FactoryBean 接口
* Created by N3verL4nd on 2017/3/20.
*/
public class CarFactoryBean implements FactoryBean<Car> {

private String brand;

public void setBrand(String brand) {
this.brand = brand;
}

public void fun() {
System.out.println("CarFactoryBean::fun()");
}

//返回bean的对象
@Override
public Car getObject() throws Exception {
Car car = new Car(brand, 500000);
return car;
}

//返回bean的类型
@Override
public Class<?> getObjectType() {
return Car.class;
}

//返回bean是不是单实例
@Override
public boolean isSingleton() {
return true;
}
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--
通过 FactoryBean 来配置 bean 的实例
class: 指向 FactoryBean 的全类名
property: 配置 FactoryBean 的属性
但实际返回的实例却是 FactoryBean 的 getObject()方法返回的实例。
-->
<bean id="car" class="com.xiya.spring.beans.factorybean.CarFactoryBean">
<property name="brand" value="BMW"/>
</bean>
</beans>


输出:

Car{brand='BMW', price=500000}

CarFactoryBean::fun()

Process finished with exit code 0

总之,当需要依赖现有的bean时,可以利用FactoryBean
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: