Spring bean 之 FactoryBean

原文地址:http://www.carlzone.cn/spring...spring

Spring中有兩種類型的Bean,一種是普通Bean,另外一種是工廠Bean,即FactoryBean。Spring FactoryBean是建立複雜的bean,通常的bean直接用xml配置便可,若是一個bean的建立過程當中涉及到不少其餘的bean和複雜的邏輯,用xml配置比較困難,這時能夠考慮用FactoryBean.
這兩種Bean都被容器管理,但工廠Bean跟普通Bean不一樣,其返回的對象不是指定類的一個實例,其返回的是該FactoryBean的getObject方法所返回的對象。在Spring框架內部,有不少地方有FactoryBean的實現類,它們在不少應用如(Spring的AOP、ORM、事務管理)及與其它第三框架(ehCache)集成時都有體現,下面簡單分析FactoryBean的用法。設計模式

一、FactoryBean用法

1)實現FactoryBean接口app

/**
 * Created by Carl on 2016/8/12.
 */
public class FactoryBeanTest implements FactoryBean<Object> {

    private boolean flag;

    public void setFlag(boolean flag){
        this.flag = flag;
    }

    // 返回這個Bean的實例
    @Override
    public Object getObject() throws Exception {
        return flag ? "carl" : new Date();
    }

    // 返回這個類類型
    @Override
    public Class<?> getObjectType() {
        return flag ? String.class : Date.class;
    }

    // 是否爲單例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

2)配置XML將Bean歸入Spring管理框架

<?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">

    <bean id="factoryBeanTest1" class="com.weimob.carl.user.dto.FactoryBeanTest">
        <property name="flag" value="true" />
    </bean>

    <bean id="factoryBeanTest2" class="com.weimob.carl.user.dto.FactoryBeanTest">
        <property name="flag" value="false" />
    </bean>

</beans>

3)Testless

public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-test.xml");
        String string = context.getBean("factoryBeanTest1", String.class);
        Date date = context.getBean("factoryBeanTest2", Date.class);
        System.out.println(string);
        System.out.println(date);
    }

}

經過簡單的測試可知,該類輸出以下:
這裏寫圖片描述ide

二、實現原理

這裏寫圖片描述
你們都知道應該知道BeanFactory在Spring IOC中的做用.它定義了Spring容器的基本方法。其中就包含getBean.由上面的方法調用圖咱們就能夠看到BeanFactory與FactoryBean的關係。下面咱們具體看一看代碼實現:
1)org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstancepost

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

    // 若是這裏不是對FactoryBean的調用,那麼結束處理
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
        throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    }

    // Now we have the bean instance, which may be a normal bean or a FactoryBean.
    // If it's a FactoryBean, we use it to create a bean instance, unless the
    // caller actually wants a reference to the factory.
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // Return bean instance from factory.
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // 這裏從FactoryBean中獲得Bean
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

2)org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean測試

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // 從cache中獲取這個對象
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 從FactoryBean獲取這個對象
                object = doGetObjectFromFactoryBean(factory, beanName);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (object != null && shouldPostProcess) {
                        try {
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                    }
                    this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
                }
            }
            return (object != NULL_OBJECT ? object : null);
        }
    }
    else {
        // 從FactoryBean獲取這個對象
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (object != null && shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

3)org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBeanthis

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
        throws BeanCreationException {
    Object object;
    try {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    @Override
                    public Object run() throws Exception {
                            // 最終調用FactoryBean.getObject()方法
                            return factory.getObject();
                        }
                    }, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 最終調用FactoryBean.getObject()方法
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    // Do not accept a null value for a FactoryBean that's not fully
    // initialized yet: Many FactoryBeans just return null then.
    if (object == null && isSingletonCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(
                beanName, "FactoryBean which is currently in creation returned null from getObject");
    }
    return object;
}

如今你們是否是對FactoryBean與BeanFactory這2個在Spring中很是重要的2個對象理解的很清楚了。spa

三、FactoryBean的存在價值

上面返回的已是做爲工廠的FactoryBean生產的產品,而不是FactoryBean自己。這種FactoryBean的機制能夠爲咱們提供一個很好的封裝機制,好比封裝Proxy、RMI/JNDI等。經過對FactoryBean實現過程的原理進行分析,相信你們會對getObject有很深入的印象。這個方法就是主要的FactoryBean的接口,須要實現特定的工廠的生產過程,至於這個生產過程是怎麼和IoC容器整合的,就是在上面的分析的內容。

四、FactoryBean與設計模式

下圖是一個典型的工廠模式的UML圖。在這裏咱們能夠看看設計模式中的工廠模式,作一個對比,以加深對這些代碼的理解。
這裏寫圖片描述對比二者的實現,能夠看到FactoryBean相似於AbstractFactory抽象工廠,getObjectForBeanInstance()方法相似於createProductA()這樣的生產接口,而具體的FactoryBean實現,如TransactionProxyFactoryBean,就是具體的工廠實現,其生成出的TransactionProxy就是"抽象工廠"模式對應的ConcreteProduct.有了抽象工廠設計模式的參考與對比。對FactoryBean的設計和實現就更容易理解一些了。

相關文章
相關標籤/搜索