三、通过 FactoryBean 来配置bean

一般情况下,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
posted @ 2017-03-20 14:17  N3verL4nd  阅读(294)  评论(0编辑  收藏  举报