• 【spring源码系列】之【FactoryBean类型的接口】


     


    1.概述

    目前我们知道,spring创建bean有多种方式,比如xml方式创建,比如@Component,@Service,@Controler,@Repository注解创建,比如@Autowired依赖注入创建,后续还有通过springboot方式的配置注解@Configuration与@Bean方式结合创建,这里不一一介绍,等分析spring boot源码的时候再做总结。

    就spring本身,提供了一种接口方式创建bean,就是本节要讨论的通过FactoryBean接口方式创建。

    2.实例

    FactoryBean接口的实现类FactoryBeanDemo:

    package com.wzj.FactoryBean;
    
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.stereotype.Service;
    
    @Service
    public class FactoryBeanDemo implements FactoryBean {
    
        @Override
        public Object getObject() throws Exception {
            return new FactoryB();
        }
    
        @Override
        public Class<?> getObjectType() {
            return FactoryB.class;
        }
    }
    

    通过FactoryBean实现类,完成自定义类FactoryB的实例化,FactoryB:

    package com.wzj.FactoryBean;
    
    import lombok.Data;
    
    @Data
    public class FactoryB {
        private String name = "wzj";
    }
    

    测试类:

    public class TestSpring {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Test
        public void testFactoryBean() {
            ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
            FactoryB factoryB = (FactoryB)applicationContext.getBean("factoryBeanDemo");
            System.out.println(factoryB);
    
            FactoryBeanDemo factoryBeanDemo = (FactoryBeanDemo)applicationContext.getBean("&factoryBeanDemo");
            System.out.println(factoryBeanDemo);
        }
    

    测试结果:

    可以看出,当获取名称为factoryBeanDemo的实例时,得到的是getObject()方法里创建的FactoryB类型的对象,而获取加前缀&factoryBeanDemo的实例时,得到的是FactoryBeanDemo本身的实例。

    3.源码

    step1: FactoryBean 接口的调用入口在实例化和 IOC/DI 做完后,就会调用 FactoryBean 类型的接口如下图所示

    				// Create bean instance.
    				// 创建bean实例
    				if (mbd.isSingleton()) {
    					sharedInstance = getSingleton(beanName, () -> {
    						try {
    							return createBean(beanName, mbd, args);
    						}
    						catch (BeansException ex) {
    							// Explicitly remove instance from singleton cache: It might have been put there
    							// eagerly by the creation process, to allow for circular reference resolution.
    							// Also remove any beans that received a temporary reference to the bean.
    							destroySingleton(beanName);
    							throw ex;
    						}
    					});
    					// FactoryBean的调用入口
    					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    				}
    

    step2: 如果要获取到 FactoryBean 类本身,就必须加上&符号,比如 beanFactory.getBean("&beanName") ,如下:

    	protected Object getObjectForBeanInstance(
    			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    
    		// Don't let calling code try to dereference the factory if the bean isn't a factory.
    		// 如果为name不为空,且以前缀&打头,直接返回bean本身
    		if (BeanFactoryUtils.isFactoryDereference(name)) {
    			if (beanInstance instanceof NullBean) {
    				return beanInstance;
    			}
    			if (!(beanInstance instanceof FactoryBean)) {
    				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
    			}
    			if (mbd != null) {
    				mbd.isFactoryBean = true;
    			}
    			return beanInstance;
    		}
    
    		// 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)) {
    			return beanInstance;
    		}
    
    		
    
    	public static boolean isFactoryDereference(@Nullable String name) {
    		return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
    	}
    
    	String FACTORY_BEAN_PREFIX = "&";
    

    stet3: BeanFactory.getBean("beanName")只能获取到 getObject()方法返回的实例。getObject 方法返回的实例会有单独的缓存存储,跟其他实例不是同一个缓存,对应的缓存是:factoryBeanObjectCache

                    // 如果是不是以前缀&打头,并且是FactoryBean类型的
    		Object object = null;
    		if (mbd != null) {
    			mbd.isFactoryBean = true;
    		}
    		else {
    			// 从缓存里拿FactoryBean实例
    			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());
    			// 缓存没有的话,
    			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    		}
    		return object;
    	}
    
    	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    		if (factory.isSingleton() && containsSingleton(beanName)) {
    			synchronized (getSingletonMutex()) {
    				// 先从缓存factoryBeanObjectCache取
    				Object object = this.factoryBeanObjectCache.get(beanName);
    				// 如果缓存为空,
    				if (object == null) {
    					// 调用getObject方法
    					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 (shouldPostProcess) {
    							if (isSingletonCurrentlyInCreation(beanName)) {
    								// Temporarily return non-post-processed object, not storing it yet..
    								return object;
    							}
    							beforeSingletonCreation(beanName);
    							try {
    								object = postProcessObjectFromFactoryBean(object, beanName);
    							}
    							catch (Throwable ex) {
    								throw new BeanCreationException(beanName,
    										"Post-processing of FactoryBean's singleton object failed", ex);
    							}
    							finally {
    								afterSingletonCreation(beanName);
    							}
    						}
    						if (containsSingleton(beanName)) {
    							// 最后放到缓存中
    							this.factoryBeanObjectCache.put(beanName, object);
    						}
    					}
    				}
    				return object;
    			}
    		}
                    ......
    

    小结:具体代码参考 getSingleton 方法之后 getObjectForBeanInstance

    • 如果bean实例不是 FactoryBean 类型的或者 name 以&开始的则直接返回实例。
    • 如果bean是 FacotyBean 并且不是以&开头, 会通过方法doGetObjectFromFactoryBean 调用FactoryBean 内部继承实现的 getObject 方法,并且判断一级缓存中如果存在该 bean 实例把实例缓存到factoryBeanObjectCache 对应的 map 中,这个是单独缓存 FactoryBean 类型实例的 map。

    4.总结

    灵活创建所需实例对象的时候,通过实现FactoryBean接口的getObject方法定义实例化过程。

    比如MyBatis提供mybatis-spring项目中的 org.mybatis.spring.SqlSessionFactoryBean

    public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    	// ...省略其他代码
    	
    	public SqlSessionFactory getObject() throws Exception {
    	if (this.sqlSessionFactory == null) {
    	  afterPropertiesSet();
    	}
    
    	return this.sqlSessionFactory;
    	}
    }
    
    

    sqlSessionFactory是SqlSessionFactoryBean的一个属性,它的赋值是在通过回调afterPropertiesSet()方法进行的。 因为SqlSessionFactoryBean实现了InitializingBean接口,所以在Spring初始化Bean的时候,能回调afterPropertiesSet()方法。

    public void afterPropertiesSet() throws Exception {
        // buildSqlSessionFactory()方法会根据mybatis的配置进行初始化。
    	this.sqlSessionFactory = buildSqlSessionFactory();
    }
    
    

    在上面的afterPropertiesSet()方法中,buildSqlSessionFactory()方法会根据mybatis的配置,完成客户所需要的的sessionFactory的初始化。


    __EOF__

    本文作者小猪爸爸
    本文链接https://www.cnblogs.com/father-of-little-pig/p/16367549.html
    关于博主:不要为了技术而技术,总结分享技术,感恩点滴生活!
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
  • 相关阅读:
    大数据开发集群搭建
    【XGBoost】第 7 章:使用 XGBoost 发现系外行星
    LeetCode_多指针_二分搜索_中等_792.匹配子序列的单词数
    Github贡献PR六部曲
    IOC底层核心原理
    pg ash自制版 pg_active_session_history
    【Linux03-基本工具之GCC】Linux下的C语言编译器
    【Unity】Unity C#基础(十四)注释
    数据链路层
    java计算机毕业设计ssm贫困区教育资源捐赠平台element vue前后端分离
  • 原文地址:https://www.cnblogs.com/father-of-little-pig/p/16367549.html