spring 循环依赖 (仅供参考)

文章目录

循环依赖产生得原因

A 中有B,B中有A,互相调用 导致循环 :
在这里插入图片描述

解决得方案

其主要得思想就是:
实列化和初始化 分开进行处理 处理得时候 采用了三级缓存进行处理得
一级缓存 :存放完成了实列化和初始化得完整对象
二级缓存 :存放只完成初始化未完成实例化得对象
三级缓存 :存放得objectFactory对象 主要是lambda表达式 用于处理aop操作, 如果不进行aop操作 一二级缓存即可完成循环问题。

下图为 三级缓存得定义图:
按照大家得叫法 顺序是 1 3 2级缓存
在这里插入图片描述
三级缓存的 objectfactory 是函数式接口
在这里插入图片描述

粗略总结的施行流程

总结:
首先 拿到一个需要创捷的对象
getbean-》dogetbean-》执行dogetbean里面的getSingleton  (这一步就是 去缓存里面看看 也没有)  由于没有得到空的
-》执行getSingleton(beanName+lambda表达式(这个表达式里面有个createBean)
-》执行getobject(就是执行刚刚的lambda表达式)
-》执行createBean 
-》执行docreateBean 这一步 通过执行createBeanInstance 这个方法就完成了对A对象的实列化(有了地址)
-》往下走 有个if  如果这个是单列 并且正在创建之中 就会进入这个方法addSingletonFactory(beanName, () -> getEarlyBeanReference
-》执行addSingletonFactory  就向三级里面添加了A的信息了
-》执行populateBean  给属性赋值  
-》执行applyPropertyValues 开始一次循环给A里面属性赋值
-》执行resolveValueIfNecessary
-》执行resolveReference
-》执行getBean 此时为了得到B对象 因为要给A对象里面的b属性赋值
-》由于B也是第一次来  和前面一样getbean-》dogetbean-》。。。applyPropertyValues
-》执行applyPropertyValues  这一步是开始给B的a属性赋值了 又是一样的步骤。。。直到
-》执行doGetBean
-》执行getSingleton(这是只传name的那个)这一次不一一能拿到东西 就是那个lambda表达式  由于咱们不进行aop 所以回返回 A的dizhi
-》回到doGetBean  !!!不会执行getSingleton(beanName+lambda表达式 直接结束返回return  
-》一直返回到 对于B而言的 getSingleton(beanName+lambda表达式 ) 因为我们是 通过这个进入的creaBean
-》在getSingleton(beanName+lambda表达式 )下  往下走执行addSingleton(beanName, singletonObject)
-》执行addSingleton(beanName, singletonObject) 往1级里面加B
然后一路返回 又到了  对于A而言的getSingleton(beanName+lambda表达式 )
-》 往下走执行addSingleton(beanName, singletonObject)
-》执行addSingleton(beanName, singletonObject) 往1级里面加A
然后一路返回 结束 完成A的实列化+初始化


稍微细致总结的流程(看小标题就是大致流程)

流程 前面得就不说了 如 refresh里面得一些准备
我们直接到 倒数第二个方法:
finishBeanFactoryInitialization
进入到这个方法 随后 没有什么说的直接进入最后得 preInstantiateSingletons
进入preInstantiateSingletons
顾名思义进行值得导入
		里面会遇到getbean  进去到了 doGetbean
进入doGetBean
里面有个 getSingleton 这个方法 (三级缓存里面找)
进入getSingleton
由于你是第一次进来 缓存里面什么也没有
null  然后 很多判断进不去  
执行完毕回到 doGetBean
执行完上一条回到doGetBean
接着 重点来了  往下走又进入了getSingleton 这个方法 不过这次  多传了一个参数:
	beanname+lamda表达式 然后进去看看
进入getSingleton(beanname+lamda)
lamda表达式: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;
						}
					});
进入这个方法  
			他会执行 getonject  由于 说过了 这是个函数式接口  
			调用getobject 就会直接使用lambda表达式

在这里插入图片描述

进入getObject
就是调用 createBean
进入createBean
	然后我们跟进这个方法:
		最后会执行到 docreateBean这个方法
进入docreateBean
	里面有个 createBeanInstance 方法 这个方法就是对 对a对象进行实列化  还没有初始化 给了个地址  至于如何实现得 就是进行反射完成得
此时完成 A 的实列化有了地址
进入addSingletonFactory(beanName, () -> getEarlyBeanReference…

在这里插入图片描述

	这个方法里面就是看一级缓存也没有 A
	然后 存到三级缓存
	二级缓存删除 (第一次来二级缓存也没有这玩意)
注意此时三级缓存 有东西了

在这里插入图片描述

执行完上一条回到doCreateBean

执行里面得populateBean 属性填充

进入 populateBean

里面一堆属性判断 执行完 走最后一个applyPropertyValues (真的进行属性注入得操作)

进入 applyPropertyValues

现在就是再给a 里面得b 进行属性得注入
里面有一个方法:valueResolver.resolveValueIfNecessary(pv, originalValue);

进入 resolveValueIfNecessary
里面就是根据你传得 originnalValue得类型判断
				走的是RuntimeBeanReference 进去就会进入
				resolveReference(argName, ref)这个方法
进入 resolveReference
然后又会执行里面得 getBean(resolvedName) 传的是 你的属性名字(b)

在这里插入图片描述

进入 getBean
然后跟进去  又遇到了 dogetbean 
进入doGetBean
然后又是和上面讲得一样得流程 不过这次换的是  b来实列化
进入getSingleton
现在三级缓存虽然有对象 但是传过来的是b  所以还是没什么效果
执行完上一条回到doGetBean
继续往下执行  又会遇到getSingleton(beanname+lamda)    
进入getSingleton(beanname+lamda)
又会遇到   singletonFactory.getObject()
进行跟进  
进入getObject
会继续运行 进入到 createBean
进入 createBean
然后我们跟进这个方法:
最后会执行到 docreateBean这个方法
进入 doCreateBean
			这次就直接到  createBeanInstance   这个方法之后
执行完createBeanInstance
此时 b对象有了地址 完成实列化
执行完  b就完成了实列化 但是 a得值 是空得

进入addSingletonFactory(beanName, () -> getEarlyBeanReference…

这个方法里面就是看一级缓存也没有 B
然后 存到三级缓存
二级缓存删除 (第一次来二级缓存也没有这玩意)
注意此时三级缓存 第二次有东西了

在这里插入图片描述

执行完上一条回到doCreateBean
然后继续执行 populateBean 
进入populateBean
继续执行  applyPropertyValues 
进入 applyPropertyValues
提醒一下 现在再给 B的a属性赋值
也是一样得操作 又执行 resolveValueIfNecessary(pv, originalValue)这个方法
进入 resolveValueIfNecessary
此时的pv值是a了 跟进resolveReference
resolveReference(argName, ref)
进入 resolveReference
跟进 又进入  getbena  
进入getBean
又进入 dogetbean 
进入doGetBean
不过到了这里就不一样了!!此时传入的参数是A
getSingleton这个方法  重要了
进入getSingleton!!!重要!!!(参数是A)
再次提醒 现在在给 B对象的a属性值赋值
这次进入 getSingleton 我们的三级缓存有A的值了

在这里插入图片描述

会进入到这个if里面

在这里插入图片描述

进入singletonFactory.getObject
上面说过这个lambda 表达式 对应的是一个方法  (getEarlyBeanReference)
然后我们跟进:
进入 getEarlyBeanReference
里面就是 看你需不需要 进行 aop操作了  不进行操作得话 直接就返回 a得地址给你了
得到地址 我们又调回去
执行完上一条回到getSingleton
继续往下执行
三级缓存里面的A 对应的值就没有了
此时的缓存情况:

在这里插入图片描述

执行完上一条回到doGetBean
由于执行完上一步 得到了a的半成品值
这次就不会进入到 进入getSingleton(beanname+lamda)    
直接执行完毕返回
执行完上一条回到resolveReference
提醒一下 现在算是完成了 对B对象的a属性的赋值
执行完上一条回到resolveValueIfNecessary
执行完上一条回到applyPropertyValues

在这里插入图片描述

执行完这个 就完成了给B的a赋值

执行完这个 就完成了给B的a赋值

在这里插入图片描述

执行完上一条回到populateBean
执行完上一条回到doCreateBean
注意此时还是在给B的a属性赋值哦
进入到一个getSingleton(name=b,flase)

在这里插入图片描述

由于此时只有三级里面有b 所以什么也得不到 不用管会往下走
回到doCreateBean
执行完doCreateBean回到 createBean
执行完createBean回到doGetBean
执行完getSingleton里面的getObject回到getSingleton
此时的递归回到了  最开始的位置了 
解释一下 
现在是在getSingleton里面  传入的参数是b
为什么是b  因为 要给A的b参数赋值
继续往下执行 会执行到

在这里插入图片描述

进入addSingleton

在这里插入图片描述

这个很简单  就是把B放进一级缓存 二三级全删

在这里插入图片描述

此时的缓存发生变化了!!!!

在这里插入图片描述

执行完上一条回到getSingleton
执行完上一条回到doGetBean
又回到getbean
回到resolveReference
提醒 为什么进入这个 是因为要给A对象的b属性赋值
往下走就执行完了  因为我们早就得到了B的实列化和初始化
回到resolveValueIfNecessary
回到applyPropertyValues
注意这个方法

在这里插入图片描述

执行前:

在这里插入图片描述

执行后

在这里插入图片描述

回到populateBean
会执行这个方法 注意此时传进来的是a

在这里插入图片描述

首先看看目前的缓存长什么样

在这里插入图片描述

进入getSingleton(beanName, false)!!!!!
注意上次也进来过  当时是name=b 
看里面的代码:

在这里插入图片描述

这一次与name=b进来不一样  这次进来name=a  并且从二级里面拿到了对象
回到doCreateBean
回到createBean
回到getSingleton
进入addSingleton(beanName, singletonObject)
这个就是把 A对象 放到一级缓存里面去

在这里插入图片描述
在这里插入图片描述

注意此时的缓存情况又变了:

在这里插入图片描述

回到getSingleton
回到doGetBean
回到getbean
回到preInstantiateSingletons
至此完了A的实列化+赋值
现在轮到B
老规矩 进入getbean->doGetBean
进入getSingleton
不过这次 一级缓存又 B的完成对象
结束

补充 (嫖的资料)

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖指的是在Spring中,多个Bean之间存在相互依赖的情况。具体来说,当一个Bean A依赖于另一个Bean B,同时Bean B也依赖于Bean A时,就形成了循环依赖。这种情况下,Spring需要解决Bean的创建和依赖注入的顺序问题。 在Spring中,循环依赖问题是由于Bean的生命周期所引起的。Spring的Bean生命周期包括了Bean的实例化、属性注入、初始化以及销毁等过程。当出现循环依赖时,Spring会通过使用“提前暴露”的方式来解决这个问题。 具体来说,当Spring创建Bean A时,发现它依赖于Bean B,于是会创建一个A的半成品对象,并将其暂时放入一个缓存中。然后,Spring会继续创建Bean B,并将其注入到A的属性中。接着,Spring会继续完成B的创建,并将其放入缓存中。最后,Spring会将A的半成品对象交给B进行依赖注入,完成A的创建,并将其从缓存中移除。 需要注意的是,Spring循环依赖有一定的限制条件。例如,如果Bean A和Bean B都是单例模式,那么它们之间的循环依赖是无法解决的。因为单例模式下,Bean的创建和依赖注入是同时进行的,无法通过缓存来解决循环依赖。在这种情况下,程序员需要手动调整Bean的依赖关系或使用其他解决方案来避免循环依赖的问题。 综上所述,Spring循环依赖是指在Spring中多个Bean之间存在相互依赖的情况。Spring通过使用缓存和提前暴露的方式来解决循环依赖问题,但在某些情况下有一定的限制条件需要注意。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值