spring 源码学习五: spring底层代码解决底层循环依赖问题

spring 源码学习五: spring底层代码解决循环依赖问题

这个问题想必很多同学也深受困扰,本次博主学完后立即做了总结,防止随时间淡忘。

一、什么是循环依赖?

当实例化完成之后,要开始进行初始化赋值操作,但是在赋值过程中,值的类型有可能是引用类型,需要从spring容器中获取具体的某个对象来完成赋值操作,

而此时需要引用的对象可能被创建了,也有可能没有被创建。如果创建就直接获取,没有的话则需要经历spring的创建过程,该内部对象在创建过程中又会有

其他属性的引用,加入引用包含了当前对象,但此时当前对象没有创建完成,就会从产生循环依赖问题。

通俗点就是:A 中属性引用了B,B中属性引用了A,A在引用B的过程中,B还没创建完成,此时B又引用了A,以下可以结合图来理解:

 

二、如何解决循环依赖的问题?

思路:

1、bean创建过程中实例化和初始化是分开的,具体代码地址:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

2、在实例化之后并还未完成初始化,直接获取半成品(只完成实例化的bean)的引用地址,保证引用的bean属性能被找到,简而言之就是提前把需要引用的bean给暴露出来

spring解决方式:

三级缓存:  singletonObjects一级缓存、earlySingletonObjects二级缓存、singletonFactories三级缓存,这三个缓存都是map类型。存值分别如下

原理;通过三级缓存可以提前把循环依赖的bean提前暴露出来,通过beanName的形式从缓存中获取,从而避免死循环创建。

三、spring三级缓存解决循环依赖例子

定义两个对象A,B,制造循环依赖关系A引用B,B引用A

配置XML

打断点先创建bean id = “a”的对象

此时实例化对象a,但是还未初始化,由下图看出bean a已经在内存中开辟了地址A@1504,而引用对象b还未实例化,意思就是a仅仅完成了初始化过程

 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));这一句代码,会把实例化的a对象提前放到三级缓存中(singletonFactories),并通过lambda表达式指定Bean 的 早期访问引用,用于解决循环引用

 下面该图填充属性,就是完成具体引用对象b的创建过程,同理该引用对象也会走getBean->doGetBean->createBean->doGreateBean这一套流程

此时就到了b对象到创建,同样走到pupulateBean(beanName,mbd,instanceWrapper)->applyPropertyValues(beanName, mbd, bw, pvs)具体填充属性会查找到对应到a对象

 具体applyPropertyValues获取对象a的过程代码:如下图,在该方法applyPropertyValues中 Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);交由valueResolver根据pv解析出originalValue所封装的对象

resolveValueIfNecessary -->resolveReference调用此方法的getBean,然后走getBean->doGetBean->createBean->doGreateBean进行递归操作。

最终在doGetBean的getSingleton方法从三级缓存中获取,如下图:此时会把三级缓存的lambda表达式移除,同时二级缓存put对应的a对象,因此二级缓存会存放(k:beanName,v:Objects)

 现在返回应用b到对象创建,此时b对象引用了a对象,可以看出对象b引用到a对象还是之前到地址A@1504

现在b对象已经是一个完整的对象,然后b对象拥有了a对象的完整引用,因此返回完整的b对象,从getCreateBean 返回 createBean再返回到doGetBean的getSingleton方法(在正向getBean->doGetBean->createBean->doGreateBean中doGetBean下一阶段通过getSingleton到createBean)如下图,返回到这我们已经产生了新的对象,会addSingletonObject添加到一级缓存中

 

 

 

 销毁对象到时候同时移除三级缓存

 四、一、二、三级缓存的关系

 结合三的整理下关系图:

1、首先一级二级三级缓存都没有,那么会通过doCreateBean方法创建对象,在该方法中通过以下代码会往三级缓存放入lambda表达式

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

 

2、在生产b对象的时候,会把三级缓存的lambda表达式移除,同时二级缓存put对应的a对象,因此二级缓存会存放(k:beanName,v:Objects),这里A@1546是重新debug的新地址不用在意,同时移除三级缓存中a的lambda表达式

 

 3、完成b对象的引用,并放入一级缓存

 

4、完成b对象的创建,此时a对象可以正常引用b对象,那么再把a对象通过addSingleton方法把对象a放入一级缓存

5、最终可以通过getSingletonObject在一级缓存获取

 

 

那么到这里就完整的解决了循环依赖的问题,后续会更新代理为啥必须使用三级缓存,我们现在的a和b其实可以通过二级缓存直接完成,更改源码去掉三级可以达到解决没有代理对象的循环依赖问题。

 

posted @ 2020-12-03 17:25  心思慕晨  阅读(259)  评论(0)    收藏  举报