美文网首页Spring
Spring bean 之 FactoryBean

Spring bean 之 FactoryBean

作者: carl_zhao | 来源:发表于2018-04-06 23:41 被阅读38次

    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的用法。

    1、FactoryBean用法

    1)实现FactoryBean接口

    /**
     * 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)Test

    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);
        }
    
    }
    

    通过简单的测试可知,该类输出如下:


    这里写图片描述

    2、实现原理

    这里写图片描述

    大家都知道应该知道BeanFactory在Spring IOC中的作用.它定义了Spring容器的基本方法。其中就包含getBean.由上面的方法调用图我们就可以看到BeanFactory与FactoryBean的关系。下面我们具体看一看代码实现:
    1)org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance

    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#doGetObjectFromFactoryBean

    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个对象理解的很清楚了。

    3、FactoryBean的存在价值

    上面返回的已经是作为工厂的FactoryBean生产的产品,而不是FactoryBean本身。这种FactoryBean的机制可以为我们提供一个很好的封装机制,比如封装Proxy、RMI/JNDI等。通过对FactoryBean实现过程的原理进行分析,相信大家会对getObject有很深刻的印象。这个方法就是主要的FactoryBean的接口,需要实现特定的工厂的生产过程,至于这个生产过程是怎么和IoC容器整合的,就是在上面的分析的内容。

    4、FactoryBean与设计模式

    下图是一个典型的工厂模式的UML图。在这里我们可以看看设计模式中的工厂模式,做一个对比,以加深对这些代码的理解。


    这里写图片描述

    对比两者的实现,可以看到FactoryBean类似于AbstractFactory抽象工厂,getObjectForBeanInstance()方法类似于createProductA()这样的生产接口,而具体的FactoryBean实现,如TransactionProxyFactoryBean,就是具体的工厂实现,其生成出的TransactionProxy就是"抽象工厂"模式对应的ConcreteProduct.有了抽象工厂设计模式的参考与对比。对FactoryBean的设计和实现就更容易理解一些了。

    相关文章

      网友评论

        本文标题:Spring bean 之 FactoryBean

        本文链接:https://www.haomeiwen.com/subject/lirehftx.html