万字长文,助你深度遨游 Spring 循环依赖源码实现!
一、概述
长文警告,事实上我不愿意写太长的文章,一面是太冗余,一方面读者容易疲倦,但是只要是涉及到源码级别的,就肯定篇幅不短,因为太短肯定没意义也解释不清楚,但是相信,耐心看完这个文章一定会有所收获!
最近有很多读者面试的时候都被问到了有关于Spring三级缓存的解决方案,很多读者在面试受挫之后,试着自己去读源码,试着去跟断点又发现一层套一层,一会自己就懵了,我这几天总结了一下,为了能够让读者更加的去了解Spring解决循环依赖问题,我决定从以下四个方面去讲述:
什么是循环依赖
如果不依赖于Spring自己解决循环依赖如何解决?
自己实现的方式有什么缺陷?
Spring中是如何解决循环依赖的?
二、什么是循环依赖
循环依赖直白点就是发生在两个类,你引用我,我引用你
的状态,如图:
三、如果不依赖于Spring自己解决循环依赖如何解决
以上图为例,假设,我们能够创建完成AService
之后,放置到到一个缓存中,再去注入属性!每次注入属性的时候,所需要的属性值都从缓存中获取一遍,缓存中没有再去创建不就解决了?如图所示:
总结一下上面的流程:
AService
创建完成后将自己加入到二级缓存,然后开始注入属性发现
AService
依赖BService
于是先查询一级缓存是否有数据一级缓存没有就查询二级缓存,有就返回,没有就创建BService
缓存中没有,开始实例化
BService
,然后注入内部属性!注入内部属性时发现依赖
AService
,于是先查询一级缓存是否有数据一级缓存没有就查询二级缓存,有就返回,没有就创建,很显然,二级缓存是有数据的。于是从二级缓存取出AService
注入到BService
。BService
创建完成后将自己从二级缓存挪到一级缓存,并返回。AService
获取到BService
后,注入到自己的属性中并把自己从二级缓存挪的一级缓存,返回AService
!至此,循环依赖创建完成!
那么有了上面的思路,我们如何用代码实现一遍我们的逻辑呢?
四、如果不依赖于Spring自己解决循环依赖如何解决
首先,我么肯定要定义一个类似于@Autowired
这样的注解,这里我们叫做 @MyAutowired
然乎我们需要模拟一个循环引用
以上,我们定义了一个循环引用,AService
引用BService
;而且BService
引用AService
,标准的循环引用
然后,我们就根据(三)说的思路,我们去用代码解决
结果
由上面的结果图示,我们解决了循环依赖,事实上Spring的解决方案,和我们手写的类似,但是Spring作为一个生态,它的设计和编码也是考虑的极其周全的,我们这样写虽然和Spring的最初想法时类似的,但是会出现哪些问题呢?
五、自己实现的方式有什么缺陷?
我们现在是直接注入类的对象,假设我们换了一种逻辑,如果我们注入的目标对象,是一个需要被代理的对象(比如该方法被AOP代理),我们这种写法就无能为力了,当然我们可以再创建的时候进行判断是否需要增加代理,当然这是一种方案,但是对于Spring而言,他的初衷是希望在bean生命周期的最后几步才去aop,再注入的时候就把该对象的代理逻辑给做完了,很显然不符合它的设计理念,那么Spring到底是如何解决的呢?
六、Spring中是如何解决循环依赖的?
首先,我们需要找到类再哪里实例化的,因为只有实例化了,才会执行注入的逻辑!
入口方法:
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
这一步主要就是getBean
,你试想一下,按照Spring的命名规范,这里明明是在创建Bean为什么就起个名字叫getBean
呢?他这么做肯定是有他的用意所在,他这么起名是因为,在属性注入的时候,发现依赖某一个属性并不会立即创建,而是会调用这个方法获取一遍,没有再去创建!不明白没关系,你记住这个地方,往下看!方法进入到getBean
--> doGetBean
之前手写了一遍解决循环依赖的代码,这里是不是很熟悉?这就是在缓存里面寻找对应的bean,当缓存有的时候直接返回,没有的时候才去创建!相信聪明的你一定若有所思! 这里极其重要,咱们进入到
createBean
里面
进入到
doCreateBean
里面
进入到
populateBean
方法,这里执行属性注入,同时也解决了循环依赖!
进入到
AutowiredAnnotationBeanPostProcessor.postProcessProperties
进入到
inject
进入到
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
此时别说你们我都想说一句
wo cao
终于看到希望了,这里由beanFactory.resolveDependency
获取即将要注入的对象,然后后面通过反射注入到对象里面去,我们是不是只需要知道beanFactory.resolveDependency
里面的逻辑就可以知道循环依赖的问题了?我们果断进去看看果然发现还没完:
进入到 doResolveDependency
恭喜你熬到头了,我们进入到
descriptor.resolveCandidate(autowiredBeanName, type, this)
方法:
org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate
哦吼,gentBean
,相信大家一定失忆了,你是不是在哪见过? 想想上文我让你记住的那个地方,里面是不是也是一个getBean,没错,他们俩是同一个方法,你会发现,最终需要注入的属性也会走一遍上述的逻辑从而完成属性对象的创建和获取,从而完成整个循环依赖!借用YourBatman大佬的一张图,总结一下整个解决三级缓存的逻辑
七、总结
读者可以参考着上述的源码逻辑实现对比看印象更深!
本次我们先自定义实现了一个解决循环依赖的方案,然后分析了一下缺陷,然后对比Spring源码的解决方案,相信,读到这里,屏幕前的你一定有所收获!加油!
作者:螺旋升天
链接:https://juejin.cn/post/6855454335162286094
来源:掘金
评论