【保姆级】手把手Debug循环依赖的整体流程
创始人
2024-02-19 21:53:12

我们先看一下循环依赖,这样看、

或者这样看、

一提到循环依赖基本必提三级缓存,本篇又是篇保姆级的Debug教程,详解出现循环依赖Spring处理的全过程,之前也介绍过一些与本篇相关的内容:

想瞅瞅三级缓存的庐山真面目可以移步到

【Spring源码】2.试个水先~Debug找到传说中的三级缓存

想了解更多关于三级缓存的相关内容欢迎移步到

【Spring源码】22. 关于循环依赖的N个问题

测试准备

既然要Debug,我们需要先准备几个测试类(包括两个互相依赖的实体类(BeanA.java,BeanB.java),一个配置文件(circulate.xml),还有一个程序启动类(Test.java))

BeanA.java

public class BeanA {BeanB beanB;public BeanB getBeanB () {
return beanB;}public void setBeanB ( BeanB beanB ) {
this.beanB = beanB;}
} 
复制代码

BeanB.java

public class BeanB {BeanA beanA;public BeanA getBeanA () {
return beanA;}public void setBeanA ( BeanA beanA ) {
this.beanA = beanA;}
} 
复制代码

circulate.xml

 
< beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsd" >< bean id="beanA" class="com.aqin.custom.circulate.BeanA" >< property name="beanB" ref="beanB" >< bean id="beanB" class="com.aqin.custom.circulate.BeanB" >< property name="beanA" ref="beanA" > 
复制代码

Test.java

public class Test {
public static void main ( String [] args ) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext ( "circulate.xml" ) ;BeanA bean = applicationContext.getBean ( BeanA.class ) ;System.out.println ( bean ) ;}} 
复制代码

Debug流程

断点位置

Test.java

AbstractApplicationContext.java

AbstractBeanFactory.java

AbstractAutowireCapableBeanFactory.java

正式开撕

启动

由于我们已经在关键步骤上打了断点,可以直接点击Resume按钮🔘进入下一个断点

开始初始化剩下非懒加载的单实例

点击Resume按钮🔘,来到了finishBeanFactoryInitialization()方法

再次点击Resume按钮🔘,来到了preInstantiateSingletons()方法

开始获取循环依赖中的第一个对象

再次点击Resume按钮🔘,来到doGetBean()

点击step into进入getSingleton()方法中

此时,尝试从一级缓存(singletonObjects)中获取BeanA对象

但我们可以看到此时一级缓存(singletonObjects)中并没有BeanA对象,于是接下来的判断singletonObject == null && isSingletonCurrentlyInCreation(beanName)的第一个条件满足了,我们进入isSingletonCurrentlyInCreation()查看第二个条件是否满足

进入后,发现并不满足,直接返回一个空的对象

未获取到、开始创建循环依赖中的第一个对象,并将第一个对象的工厂方法放入三级缓存

既然从缓存中获取不到,那就需要开始创建BeanA对象了,点击Resume按钮🔘,来到createBean()

继续点击Resume🔘,来到doCreateBean(),进入真正创建Bean的逻辑

点击Resume🔘,来到addSingletonFactory()

BeanName和创建该Bean的lambda表达式作为键值对放入三级缓存

由于在方法createBeanInstance()中我们已经对beanA进行了实例化(已经在内存中创建了空间BeanA@1607

循环依赖中第一个对象进行属性填充时获取第二个对象

接下来进行属性填充,于是开始尝试获取BeanB

未获取到、开始创建循环依赖中的第二个对象

BeanB也同样的未能从缓存中获取到,于是开始创建BeanB

来到createBeanInstance()中,此时beanB会进行实例化

随后添加进三级缓存

实例化后的beanB已经在内存中有了空间地址(BeanB@1730)继续进行初始化

循环依赖中的第二个对象进行属性填充时获取第一个对象

接下来调用populateBean()方法进行属性填充,populateBean()方法中向BeanB填充属性BeanA时,调用getBean()方法尝试从缓存中获取BeanA的实例

于是再次来到了doGetBean()中的getSingleton()

getSingleton()中先尝试从一级缓存(singletonObjects)中获取BeanA的实例,没获取到再尝试从二级缓存(earlySingletonObjects)中获取BeanA的实例,还是没获取到,于是来到了三级缓存(singletonFactories

获取到第一个对象存入三级缓存的工厂对象、创建了第一个对象为第二个对象填充属性

可以看到此时的三级缓存中已经不是空的了,在之前的步骤中我们把BeanABeanName和创建BeanA的lambda表达式、BeanBBeanName和创建BeanB的lambda表达式作为键值对都放入了三级缓存,所以此时获取到的工厂对象singletonFactory是不为null的,于是通过该工厂对象创建一个BeanA的单例对象,将这个对象添加进了二级缓存,并将其从三级缓存中移除

回到doGetBean()方法中,此时获取到了BeanA的实例对象BeanA@2065

将获取到的BeanA实例不断返回至刚开始调用getBean()populateBean()方法中,继续执行到最后的赋值方法applyPropertyValues()中,将获取到的实例对象BeanA@2065通过调用setPropertyValues()完成向BeanBbw对象的属性赋值操作

接下来继续调用initializeBean()方法完成实例BeanB@1730的初始化,于是我们获取到了一个完整的BeanB

返回完整的第二个对象完成第一个对象的属性填充

继续下一步会发现我们又回到了getBean()中(b_d)

别懵,还记不记得咱们的debug最先是从哪里开始的(˶‾᷄ ⁻̫ ‾᷅˵)?

自问自答:从实例化BeanA后,对其进行属性填充时,调用getBean()获取BeanABeanB属性开始的

所以,我们又回到了最开始获取BeanB属性的时候,而经过上面的一大串操作,此时返回的是一个完整的BeanB了,于是同样的,像刚刚给BeanBbw对象属性赋值的流程一样,我们进入方法applyPropertyValues()中,将获取到的实例对象BeanB@1730通过调用setPropertyValues()完成向BeanAbw对象的属性赋值操作

执行完填充属性的populateBean()方法后,就可以看到下图中的红框框中的对象,没错、解决了循环依赖的问题

回到启动类,获取到完整的BeanABeanB

Debug完成,撒个花(。・ω・。)ノ🎉🎉

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...