大家好,我是楼仔!之前有粉丝问我楼哥,你的文章怎么不研究源码,我对这个挺感兴趣的。其实我不是很喜欢研究源码,热衷于理论和实操。不过转念一想,我已经写了很多系列文章,也可以发布一系列源码,满足不同爱好者的要求。让我们使用Spring。Spring是如何解决循环依赖的,网上的资料很多,但是感觉写的好的很少,尤其是源码解读方面。我会单独写一篇文章。这篇文章绝对肝!不用BB,放在文章目录下。一、基础知识1.1什么是循环依赖?一个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成循环调用,有以下三种方法。让我们来看一个简单的演示和基准测试“案例2”。@ServicepublicclassLouzai1{@AutowiredprivateLouzai2louzai2;publicvoidtest1(){}}@ServicepublicclassLouzai2{@AutowiredprivateLouzai1louzai1;publicvoidtest2(){}}这是一个经典的循环依赖,效果很好,后面我们会从源码的角度解读整体的执行过程。1.2三级缓存在解读源码流程之前,必须先了解一下spring内部三级缓存的逻辑,否则后面代码会乱码。一级缓存:singletonObjects,用于保存实例化、注入、初始化的bean实例;二级缓存:earlySingletonObjects,用于保存实例化的bean实例;三级缓存:singletonFactories,用来保存bean的创建工厂,以便后面有机会创建代理对象。这是核心,我们直接看源码:执行逻辑:先从“一级缓存”中查找对象,找到则返回,找不到则查找“二级缓存”;找到“二级缓存”,存在则返回,不存在则返回找到“三级缓存”;寻找“三级缓存”,如果找到了,获取对象,放入“二级缓存”,从“三级缓存”中移除。1.3原理执行过程我将“case2”的执行过程分解为以下3个步骤,是不是和“套娃”类似?整个执行逻辑如下:第一层,先获取A的Bean,没有则准备创建一个,然后将A的代理工厂放入“三级缓存”(这个A实际上是一个半成品,还没有加工完成,注入里面的属性),但是A依赖于B的创建,所以必须先创建B;在第二层,你要创建B,但是你发现B依赖于A,你需要先创建A;在第三层,去创建A,因为在第一层已经创建了A的代理工厂,直接从“三级缓存”中获取A的代理工厂,获取A的代理对象,放入“二级缓存”,清除“三级缓存”;回到第二层,现在有了A的代理对象,完美解决了对A的依赖(这里的A还是个半成品),B初始化成功;回到第一层,现在B初始化成功,完成A对象的属性注入,然后填充A的其他属性,A的其他步骤(包括AOP),完成A的完整初始化功能(这里,A是一个完整的Bean)。将A放入“L1缓存”。为什么要使用三级缓存?先看源码执行过程,后面会给出答案。2.源码解读注意:Spring版本是5.2.15.RELEASE,否则和我的代码不一样!!!以上知识其实网上都有。以下是我们的亮点。让你跟着娄子走一遍代码流程。2.1代码入口这里需要多跑几次,跳过前面的beanName,只看louzai1。2.2第一层进入doGetBean(),getSingleton()没有找到对象,进入创建Bean的逻辑。进入doCreateBean()后,调用addSingletonFactory()。将louzai1的工厂对象插入到三级缓存singletonFactories中。进入populateBean(),执行postProcessProperties(),这里是一个策略模式,找到下图中的策略对象。正式进入策略对应的方法。下面都是获取louzai1的成员对象,然后注入。进入doResolveDependency(),找到louzai1依赖的对象名。Louzai2需要获取louzai2的bean,就是AbstractBeanFactory的方法。正式拿到louzai2的bean。至此,第一层玩偶基本结束,因为louzai1依赖louzai2,现在进入第二层玩偶。2.3第二层获取louzai2的bean,从doGetBean()到doResolveDependency(),和第一层的逻辑完全一样,找到louzai2依赖的对象名louzai1。前面的流程全部省略,直接进入doResolveDependency()。正式拿到louzai1的bean。至此,第二层娃娃就结束了,因为louzai2依赖louzai1,所以我们进入第三层娃娃。2.4第三层获取louzai1的bean。在第一层和第二层,我们每次都是从getSingleton()中获取对象,但是因为louzai1和louzai2的三级缓存之前没有初始化,所以获取到的对象是空的。击中关键!击中关键!!击中关键!!!在第三层,由于三级缓存中有louzai1的数据,所以这里使用三级缓存中的工厂为louzai1创建一个代理对象,并塞入二级缓存中。这里获取到louzai1的代理对象,解决louzai2的依赖,返回第二层。2.5回到二楼回到二楼后,louzai2的初始化就完成了。这就是结局?二级缓存中的数据什么时候交给一级呢?别着急,看这里,记住在doGetBean()中,我们会通过createBean()创建一个louzai2的bean,当louzai2的bean创建成功后,我们会执行getSingleton(),它会处理louzai2的结果.当我们进入getSingleton()时,我们会看到下面的方法。这里是louzai2处理一级缓存和二级缓存的逻辑,清除二级缓存放入一级缓存。2.6返回第一层同2.5,louzai1初始化完成后,会清空louzai1的二级缓存,对象放入一级缓存。至此,所有流程结束,我们返回louzai1的对象。3、原理深入解读3.1为什么要有三级缓存?这是一道非常经典的面试题。我已经告诉你详细的执行过程,包括源码解读,但是没有告诉你为什么要用三级缓存?这就是重点!敲黑板!!!再说说“一级缓存”的作用。变量名为singletonObjects,结构为Map
