• 8.5 Spring解决循环依赖的原理(非AOP)


    Spring为什么可以解决set + singleton模式下循环依赖

    根本的原因在于:这种方式可以做到将“实例化Bean”和给""Bean属性赋值"这两个动作分开去完成。

    实例化Bean的时候:调用无参数构造方法来完成。 此 时 可 以 先 不 给 属 性 赋 值 , 可 以 提 前 将 该 B e a n 对 象 “ 曝 光 ” 给 外 界 。 \color{red}此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。 Bean

    给Bean属性赋值的时候:调用setter方法来完成。

    两 个 步 骤 是 完 全 可 以 分 离 开 去 完 成 的 , 并 且 这 两 步 不 要 求 在 同 一 个 时 间 点 上 完 成 。 \color{red}两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成。

    针对

    <bean id="a" class="com.cjf.bean.A">
        <property name="b" ref="b"/>
    bean>
    <bean id="b" class="com.cjf.bean.B">
        <property name="a" ref="a"/>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1、实例化/初始化

    实例化

    • 内存中申请一块存储空间

    初始化属性填充

    • 完成属性的各种赋值

    2、3个Map&4大方法

    DefaultSingletonBeanRegistry类有三个重要的缓存

    一级缓存

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    • 1

    一级缓存存储的是:完整的Bean对象,也就是说这个缓存中的Bean对象的属性都已经复制了。只一个完整的Bean对象

    二级缓存

    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    • 1

    二级缓存存储的是:早期的单例Bean对象,这个缓存中的单例Bean对象的属性没有赋值,只是一个早期的单例对象

    三级缓存

    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    • 1
    • 三级缓存存储的是:
      • 单例工厂对象,这个里面存储了大量的工厂对象,每一个单例Bean对象都会对应一个单例工厂对象。
    • 这个集合中存储的是:
      • 创建该单例对象时对应的哪个对应的那个单例工厂的对象(在这里存放的是beanlambda的工厂)

    AbstractAutowireCapableBeanFactory类的

    • doCreateBean(2),创建bean

    • populateBean(3),给实例化后的bean进行属性赋值

    DefaultSingletonBeanRegistry类的

    • getSingleton(1),主要一个是创建bean对象,创建完最后走(4)。(还有一个是从缓存中获取Bean对象)

    • addSingleton(4),加入到一级缓存,并删除二/三级缓存的中Bean

    3、A/B对象在三级缓存中的迁移说明

    这里过程最好记住(有助于理解源码)

    1. 先去一级缓存寻找 A ,没有去创建 A, 然后 A 将自己放到三级缓存中,初始化的时候需要 B ,去创建 B
    2. B 实例化同理 AB 放入到了 三级缓存),B 初始化的时候发现需要 A ,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 A 然后把三级中的 A 放入二级缓存里面,并删除三级缓存中的 A
    3. B 顺利初始化完毕,将自己放到一级缓存里面(此时 B 里面的 A 依然是创建中的状态),再删除三级缓存中的 B 和 尝试去删除二级缓存中的 B (此时二级缓存中只有 A
    4. 然后回来接着创建 A ,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成 A 创建,再从 二级缓存中获取 A 对象, 并将 A 自己放入到一级缓存里面,再删除三级缓存中的 A 和 删除二级缓存中的 A

    4、源码分析-1

    以下一句话

    • 先去一级缓存寻找 A ,没有去创建 A, 然后 A 将自己放到三级缓存中,初始化的时候需要 B ,去创建 B

    我们直接进入debug进入AbstractBeanFactory工厂的doGetBean方法。

    我们发现会首先从 getSingleton(beanName) 方法中先去尝试获取共享的实例化对象。

    进入该方法,首先会去从一级缓存中获取该 bean 对象,此时为 bean 对象空并且该 bean 对象没有在创建中(就是实例化完成的对象,没有被赋值),直接结束方法,直接返回一个 null 对象

    image-20221112135056019

    • 我们可以进入isSingletonCurrentlyInCreation(String beanName) 方法,可以看出这是从一个set 集合中获取是否这个在创建中的对象,这里这个set里面存放的是是刚刚实例化完成的对象,还没有初始化的bean对象的名称

    • 	private final Set<String> singletonsCurrentlyInCreation =
      			Collections.newSetFromMap(new ConcurrentHashMap<>(16));
      	//........
      	public boolean isSingletonCurrentlyInCreation(String beanName) {
      		return this.singletonsCurrentlyInCreation.contains(beanName);
      	}
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

    然后我们直接走到doCreateBean 方法中的324行获取单例对象的方法 getSingleton(....) ,返回一个实例化的Bean对象

    image-20221112141222948

    此时真正的开始创建Bean对象,

    image-20221112142113089

    进入 createBean 方法

    其中重点是

    try {
        	//真正的去创建bean对象,返回一个实例化完成的对象
    		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    		if (logger.isTraceEnabled()) {
    			logger.trace("Finished creating instance of bean '" + beanName + "'");			}
    		return beanInstance;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    image-20221112142104221


    进入 AbstractAutowireCapableBeanFactory doCreateBean 方法,我们可以发现这里正是 bean 对象的 生命周期前7步执行的方法。关于生命周期的讲解可以看7.6、bean的周期10步源码解析

    image-20221112143428510

    关于bean的实例化可以去看Spring源码:bean创建(三):createBeanInstance_

    这里我们是通过缺省构造器来获得bean对象的

    将返回的bean对象(a),通过 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 方法放入到 三级缓存 中。

    (这里三级缓存存放的是 k :a,v :lambda(一个单例工厂))

    image-20221112151236681

    然后对bean对象(a)进入属性赋值对于如何赋值的可以看 Spring源码:bean创建(四)属性注入

    此时我们已经将 a 对象放入到了三级缓存中,然后进入 doCreateBean 方法 populateBean(beanName, mbd, instanceWrapper);bean 对象进入赋值

    image-20221112153701044

    进入applyPropertyValues 方法

    	protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    		//.....
    		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
     
    		// 遍历属性,并将配置的值转换为对应的类型
    		List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
    		boolean resolveNecessary = false;
            //这里主要是解析到所有的属性PropertyValue的list集合,我们需要遍历集合解析属性
    		for (PropertyValue pv : original) { 
    			if (pv.isConverted()) {
    				deepCopy.add(pv);
    			}
    			else {
    				String propertyName = pv.getName();//属性的名称:b
    				Object originalValue = pv.getValue();//属性的值: 
    				//解析原始值,例如RuntimeBeanReference类型,会获取BeanFactory中其真正的bean;
                    //String或TypedStringValue类型会视为一个表达式进行求值等。
    				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
    				Object convertedValue = resolvedValue;
    				//是否能进行类型转换 = 可以写入 && 没有'.'或者'[' 符号
    				boolean convertible = bw.isWritableProperty(propertyName)
                        &&!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
    				if (convertible) {
    					//将value转换为对应的类型
    					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
    				}
     
    				//为了避免每次创建bean的时候都重复转换类型,
                    //Spring会尽可能地将转换好的值放入对应的BeanDefinition中
    				if (resolvedValue == originalValue) {
    					//如果原始值并未进行解析
    					if (convertible) {
    						pv.setConvertedValue(convertedValue);
    					}
    					deepCopy.add(pv);
    				}
     
    		// 将解析、转换好的属性设置到BeanWrapper中
    		//之所以bean需要BeanWrapper包装一次,也是为了方便给bean填充属性
    		try {
    			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    		}
    		catch (BeansException ex) {
    			throw new BeanCreationException(
    					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    我们重点进入 valueResolver.resolveValueIfNecessary(pv, originalValue); 方法

    //返回一个属性对象(此时我们要去获取 b 对象)
    Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
    
    • 1
    • 2

    image-20221112154935590

    直接进入 resolveReference(argName, ref);

    重点 bean = this.beanFactory.getBean(resolvedName); 返回一个bean对象,

    image-20221112155149133

    直接进入 bean = this.beanFactory.getBean(resolvedName); 方法

    我们发现又回到了doGetBean 方法,更 A 对象一样的去获取 B 对象

    image-20221112155410580

    一句话

    • 先去一级缓存寻找 A ,没有去创建 A, 然后 A 将自己放到三级缓存中,初始化的时候需要 B ,去创建 B

    5、源码分析-2

    以下一句话

    • B 实例化同理 A (实例化后, B 放入到了 三级缓存),B 初始化的时候发现需要 A ,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 A 然后把三级中的 A 放入二级缓存里面,并删除三级缓存中的 A

    我们跳到对 b 对象的赋值操作,去获取 b 对象的属性 a 对象

    //去获取 a 对象
    Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
    
    • 1
    • 2

    在进入 BeanDefinitionValueResolverresolveReference(argName, ref);

    image-20221112160508324

    同理的 进入 resolveReference 方法去获取 a 对象

    	@Nullable
    	private Object resolveReference(Object argName, RuntimeBeanReference ref) {
            //...
            bean = this.beanFactory.getBean(resolvedName);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    image-20221112161124161

    我们此时到了 getSingleton(String beanName, boolean allowEarlyReference) 方法中。

    我们获取 A 对象,先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 A 然后把三级中的 A 放入二级缓存里面,并删除三级缓存中的 A

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
       // Quick check for existing instance without full singleton lock
       Object singletonObject = this.singletonObjects.get(beanName);
       if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
          singletonObject = this.earlySingletonObjects.get(beanName);
          if (singletonObject == null && allowEarlyReference) {
             synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                 //先从一级缓存获取
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    //再从二级缓存获取
                   singletonObject = this.earlySingletonObjects.get(beanName);
                   if (singletonObject == null) {
                       //再从三级缓存获取 a 的工厂
                      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                      if (singletonFactory != null) {
                          //从整个的三级缓存(单例工厂)中获取 a 对象然后放入二级缓存,然后从当前三级缓存删除该对象
                         singletonObject = singletonFactory.getObject();
                         this.earlySingletonObjects.put(beanName, singletonObject);
                         this.singletonFactories.remove(beanName);
                      }
                   }
                }
             }
          }
       }
       return singletonObject;//返回bean对象
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    6、源码分析-3

    以下一句话

    • B 顺利初始化完毕,将自己放到一级缓存里面(此时 B 里面的 A 依然是创建中的状态),再删除三级缓存中的 B 和 尝试去删除二级缓存中的 B (此时二级缓存中只有 A

    从以上获取到 bean 对象后(a) ,一层一层的返回,

    此时 Object resolvedValue = a 对象,将 a 对象赋值给 b 对象的 a 属性

    	// 将解析、转换好的属性设置到BeanWrapper中
    	//之所以bean需要BeanWrapper包装一次,也是为了方便给bean填充属性
    	try {
    		bw.setPropertyValues(new MutablePropertyValues(deepCopy));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们发现 b 的包装类 BeanWrapper 中含有了 a 对象

    image-20221112162522893

    此时 b 完成了对属性的赋值

    	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {	
            //....
            //此时完成了这一步赋值
    		populateBean(beanName, mbd, instanceWrapper);
            //然后接着去完成 b 对象的后续生命周期
    		exposedObject = initializeBean(beanName, exposedObject, mbd);
            //...
            return exposedObject;//返回 b 对象
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    不断的返回到

    singletonObject = singletonFactory.getObject();
    
    • 1

    image-20221112163809315

    	protected void addSingleton(String beanName, Object singletonObject) {
    		synchronized (this.singletonObjects) {
                //将 b 对象加入到 一级缓存中
    			this.singletonObjects.put(beanName, singletonObject);
    			//删除三级缓存中的 b 对象
    			this.singletonFactories.remove(beanName);
                //尝试去删除二级缓存中的 b 对象
                //(此时二级缓存中只有 a 对象)
    			this.earlySingletonObjects.remove(beanName);
    			this.registeredSingletons.add(beanName);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    image-20221112165429440

    此时B 顺利初始化完毕,将自己放到一级缓存里面(此时 B 里面的 A 依然是创建中的状态),再删除三级缓存中的 B 和 尝试去删除二级缓存中的 B (此时二级缓存中只有 A

    7、源码分析-4

    以下一句话

    • 然后回来接着创建 A ,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后 完成创建, 并将 A 自己放入到一级缓存里面

    从以上不断的返回至

    Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
    
    • 1

    a 对象属性的 b 属性赋值为 resolvedValue

    image-20221112170131263

    其中 的 BeanWrapperImpl 就是 a 的包装类

    image-20221112170155746

    a 对象注入属性完毕之后,然后去从缓存中获取 a 对象,此时 a 对象直接从二级缓存中获取

    image-20221112193733335

    此时将返回的 a 对象放入到一级缓存中,删除三级缓存中的 a (此时三级缓存中没有 a),删除二级缓存中的a

    image-20221112195325994

    最后完成了 a b 对象的依赖(最后全部放入到了一级缓存

    image-20221112200711705

    8、小总结

    通过源码分析,其实单纯的循环依赖二/一级缓存也可以解决(只不过一级会覆盖中间态),但是用三级缓存有其他的作用(主要为了解决aop增强的问题,所以三级缓存的ValueobjectFactory)。


    • Spring 创建bean主要分为两个步骤,创建原始bean对象,接着去填充对象属性和初始化

    • 每次创建 bean之前,我们都会从缓存中查下有没有该bean,因为是单例,只能有一个

    • 当我们创建beanA的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了beanB,接着就又去创建beanB,同样的流程,创建完beanB填充属性时又发现它依赖了beanA又是同样的流程,

    • 不 同 的 是 \color{red}不同的是

      • 这时候可以在三级缓存中查到刚放进去的原始对象beanA,所以不需要继续创建,用它注入 beanB,完成beanB的创建

      • 既然 beanB创建好了,所以 beanA就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成


    • Spring解决循环依赖依靠的是Bean的" 中 间 态 \color{red}中间态 "这个概念,
      • 而这个中间态指的是 已 经 实 例 化 但 还 没 初 始 化 的 状 态 − > 半 成 品 \color{red}已经实例化但还没初始化的状态 ->半成品 >

    实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。

    Spring为了解决单例的循环依赖问题,使用了三级级存。

    • 其中一级缓存为单例池( singletonObjects)

    • 二级缓存为提前曝光对象( earlySingletonObjects)

    • 三级缓存为提前曝光对象工厂( singletonFactories) 。

    假设AB循环引用,实例化A的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存接着去填充属性时又发现自己依赖A,这时候从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果有AOP代理,就进行AOP处理获取代理后的对象A,注入B,走剩下的流程。

    9、分析流程图

    image-20221112220034033

  • 相关阅读:
    【JVM调优实战100例】01——JVM的介绍与程序计数器
    【Python系列】Python 方法变量参数详解
    IGES文件在线渲染与转换方法
    SMP多核启动(二):PSCI
    Java:C与Java的10个主要区别
    【Python机器学习】零基础掌握isotonic_regression等渗回归
    esp32系列(5):esp32 蓝牙架构学习
    5款开源、美观、强大的WPF UI组件库
    超声波模块详细介绍(stm32循迹小车中超声波的介绍)
    基于SSM框架的图片分享及评价网站设计与实现毕业设计源码201524
  • 原文地址:https://blog.csdn.net/qq_67720621/article/details/127826245