我们先看一下循环依赖,这样看、
或者这样看、
一提到循环依赖基本必提三级缓存,本篇又是篇保姆级的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
<? xml version="1.0" encoding="UTF-8" ?>
< beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd" >
< bean id="beanA" class="com.aqin.custom.circulate.BeanA" >
< property name="beanB" ref="beanB" ></ property >
</ bean >
< bean id="beanB" class="com.aqin.custom.circulate.BeanB" >
< property name="beanA" ref="beanA" ></ property >
</ bean >
</ beans >
复制代码
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
)
获取到第一个对象存入三级缓存的工厂对象、创建了第一个对象为第二个对象填充属性
可以看到此时的三级缓存中已经不是空的了,在之前的步骤中我们把BeanA
的BeanName
和创建BeanA
的lambda表达式、BeanB
的BeanName
和创建BeanB
的lambda表达式作为键值对都放入了三级缓存,所以此时获取到的工厂对象singletonFactory
是不为null
的,于是通过该工厂对象创建一个BeanA
的单例对象,将这个对象添加进了二级缓存,并将其从三级缓存中移除
回到doGetBean()
方法中,此时获取到了BeanA
的实例对象BeanA@2065
将获取到的BeanA
实例不断返回至刚开始调用getBean()
的populateBean()
方法中,继续执行到最后的赋值方法applyPropertyValues()
中,将获取到的实例对象BeanA@2065
通过调用setPropertyValues()
完成向BeanB
的bw
对象的属性赋值操作
接下来继续调用initializeBean()
方法完成实例BeanB@1730
的初始化,于是我们获取到了一个完整的BeanB
返回完整的第二个对象完成第一个对象的属性填充
继续下一步会发现我们又回到了getBean()
中(b_d)
别懵,还记不记得咱们的debug最先是从哪里开始的(˶‾᷄ ⁻̫ ‾᷅˵)?
自问自答:从实例化BeanA
后,对其进行属性填充时,调用getBean()
获取BeanA
的BeanB
属性开始的
所以,我们又回到了最开始获取BeanB
属性的时候,而经过上面的一大串操作,此时返回的是一个完整的BeanB
了,于是同样的,像刚刚给BeanB
的bw
对象属性赋值的流程一样,我们进入方法applyPropertyValues()
中,将获取到的实例对象BeanB@1730
通过调用setPropertyValues()
完成向BeanA
的bw
对象的属性赋值操作
执行完填充属性的populateBean()
方法后,就可以看到下图中的红框框中的对象,没错、解决了循环依赖的问题
回到启动类,获取到完整的BeanA
和BeanB
Debug完成,撒个花(。・ω・。)ノ🎉🎉