zoukankan      html  css  js  c++  java
  • Spring如何解决循环依赖

    今天我们来了解下Spring是如何解决循环依赖的。

    准备工作:

    1.测试代码

    • 创建CurrentlyService1和CurrentlyService2。两个类分别依赖注入对方

    2.了解@Autowire原理。

    • 对于这次探索循环依赖,如果不知道的,就当他是针对这个属性调用createBean。

    3.ObjectFactory接口。

    • 他有一个getObject方法。其实就是为了提早曝光object

    4.DefaultSingletonBeanRegistry中的三个map。

    • singletonObjects

      存放已经创建好的bean对应的object。beanName->object

    • singletonFactories

      存放beanName对应的ObjectFactory。beanName->ObjectFactory

    • earlySingletonObjects

      存放beanName对应的ObjectFactory.getObject。beanName->object

    • singletonObjects和earlySingletonObjects对应存放的object有什么实质性区别?

    开始逐步分析

    创建bean的前面我们不分析了。直接到AbstractAutowireCapableBeanFactory#doCreateBean 实例化之后。

    1.将bean提早曝光

    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    				isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
    	if (logger.isDebugEnabled()) {
    		logger.debug("Eagerly caching bean '" + beanName +
    				"' to allow for resolving potential circular references");
    	}
    	addSingletonFactory(beanName, new ObjectFactory<Object>() {
    		@Override
    		public Object getObject() throws BeansException {
    			return getEarlyBeanReference(beanName, mbd, bean);
    		}
    	});
    }
    

    1.1 earlySingletonExposure

    当bean为单例 && 容器配置允许循环依赖 && bean正在创建

    满足以上三个条件才会解决循环依赖

    1.2 创建一个ObjectFactory并重写getObject方法

    getEarlyBeanReference走的是InstantiationAwareBeanPostProcessorAdapter#getEarlyBeanReference。
    点进去可以看到其实就是返回自己

    1.3 addSingletonFactory。将ObjectFactory放入singletonFactories

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    	Assert.notNull(singletonFactory, "Singleton factory must not be null");
    	synchronized (this.singletonObjects) {
    		if (!this.singletonObjects.containsKey(beanName)) {
    			this.singletonFactories.put(beanName, singletonFactory);
    			this.earlySingletonObjects.remove(beanName);
    			this.registeredSingletons.add(beanName);
    		}
    	}
    }
    

    我们可以看到singletonFactories添加了ObjectFactory后会把earlySingletonObjects给remove掉。这两个map其实是互斥的,有他没我,有我没他。

    2.解决循环依赖

    假设我们现在要创建CurrentlyService1,经过了1的所有步骤后(即提早曝光)。开始populateBean。

    我们要注入CurrentlyService2,其实就是创建CurrentlyService2这个bean。这时候CurrentlyService2自己走一遍1的所有步骤,再进行populateBean的时候,要注入CurrentlyService1,即创建CurrentlyService1这个bean。

    这时候我们看AbstractBeanFactory#doGetBean里的
    Object sharedInstance = getSingleton(beanName);

    里面调用getSingleton方法传的allowEarlyReference入参是true(注意这里。后续其他地方调用传的是false)

    在了解循环依赖之前。我一直以为这仅仅是从缓存中获取已经加载完成的bean。
    其实不然。我们来看下里面实现

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    	Object singletonObject = this.singletonObjects.get(beanName);
    	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    		synchronized (this.singletonObjects) {
    			singletonObject = this.earlySingletonObjects.get(beanName);
    			if (singletonObject == null && allowEarlyReference) {
    				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    				if (singletonFactory != null) {
    					singletonObject = singletonFactory.getObject();
    					this.earlySingletonObjects.put(beanName, singletonObject);
    					this.singletonFactories.remove(beanName);
    				}
    			}
    		}
    	}
    	return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
    

    首先,Object singletonObject = this.singletonObjects.get(beanName);其实就是我以前的理解,从缓存中获取已经加载完成的bean。因为CurrentlyService1还没创建完成。singletonObject自然是null。往下走earlySingletonObjects.get(beanName),这里在1.3中已经被remove掉了,也是null。往下走this.singletonFactories.get(beanName)。这里1.3中添加了所以不是null。然后就是回调singletonFactory.getObject方法。然后两个互斥map处理一下。

    这时,CurrentlyService2的属性填充(populateBean)已经处理完毕。

    看下面的代码

    if (earlySingletonExposure) {
    	Object earlySingletonReference = getSingleton(beanName, false);
    	if (earlySingletonReference != null) {
    		if (exposedObject == bean) {
    			exposedObject = earlySingletonReference;
    		}
    		.....(这部分链路不常见。不分析)
    	}
    }
    

    这时,我们又调用了getSingleton(beanName, false)方法。只不过allowEarlyReference是false。

    根据几个map最终返回的是null。至此CurrentlyService2已创建完毕。

    继续回到CurrentlyService1的创建。由于CurrentlyService2已创建完毕。CurrentlyService1的属性填充(populateBean)已经处理完毕。这时,调用getSingleton(beanName, false)。不同于CurrentlyService2的结果,返回的结果不为null。因为在创建CurrentlyService2的过程中已经把CurrentlyService2对应的互斥map给改了。

    然后将提早曝光的object返回

    bean完全创建完成后。互斥map都会清空。

    总结

    1.所有单例的bean在创建完成之前都会提早曝光。

    2.提早曝光就是预先为这个bean创建好一个ObjectFactory。一旦发现是循环依赖。就调用ObjectFactory.getObject返回实例。

    3.Spring判断循环依赖的条件是
    earlySingletonObjects里是否存在这个ObjectFactory.getObject生成的object。(或者另外一个map,两个互斥的)

  • 相关阅读:
    php 高并发
    mysql 基础明细
    关于高并发和秒杀系统,你知道的和不知道的一些事
    carbon
    自定义tarbar
    学习小参考
    lnmp1.4,400,500,错误
    PHPSTORM+Thinkphp3.2模板标签替换Thinkphp5.1公式
    Thinkphp5.1手册太简单,有的功能用起来不确定结果是否和预料的一样,顾整理记录
    CentOS7 最小化安装vmware-tools
  • 原文地址:https://www.cnblogs.com/chenshengyue/p/11399805.html
Copyright © 2011-2022 走看看