【分析向】没有三级缓存会导致什么?

【Spring源码系列- IOC】

1

【Spring源码】0.安装Gradle环境

2

【Spring源码】1.下载与编译_pom relocation to an other version number is not f

3

【Spring源码】2.试个水先~Debug找到传说中的三级缓存(图解向,堆图预警)

4

【Spring源码】3. xml文件如何转换成BeanDefinition(主要涉及prepareRefresh()+ obtainFreshBeanFactory()两个函数,图解向,堆图预警)_spring xml转bean

5

【Spring源码】4. 自己搞个标签?~自定义标签保姆级全过程(图解向,堆图预警)

6

【Spring源码】5.spring的bean工厂准备工作(prepareBeanFactory(beanFactory)

7

【Spring源码】6. Spring扩展自定义属性编辑器保姆级教程

8

【Spring源码】7. 如何添加自定义的BeanFactoryPostProcessor

9

【Spring源码】8. 捋下invokeBeanFactoryPostProcessors()主要处理流程

10

【Spring源码】9. 超级重要的ConfigurationClassPostProcessor

11

【Spring源码】10. 递归调用的processConfigurationClass()方法

12

【Spring源码】11. 我是注解类不?checkConfigurationClassCandidate()注解类判断方法详解

13

【Spring源码】12. 注册bean处理器registerBeanPostProcessors()

14

【Spring源码】13. 国际化处理initMessageSource()源码解析

【补充内容】【保姆级】SpringBoot项目中的i18n国际化

15

【Spring源码】14. 消息多播器(观察者模式)

【补充内容】【保姆级示例向】观察者模式

16

【Spring源码】15. Bean的创建过程(1.概述篇)

17

【Spring源码】16. Bean的创建过程(2)

18

【Spring源码】17.创建Bean这篇认真的@(・●・)@

【补充内容】

【保姆级·创建对象】如何通过Supplier创建对象

【保姆级·创建对象】如何通过factory-method创建对象

【保姆级·创建对象】如何利用resolveBeforeInstantiation()在预处理阶段返回一个Bean的实例对象

19

【Spring源码】18. factory-method创建对象关键函数详解:instantiateUsingFactoryMethod()

20

【Spring源码】19. 没合适的构造器?找determineCandidateConstructors()!

21

【Spring源码】20. MergedBeanDefinitionPostProcessor修改/合并bean定义

【补充内容】

【保姆级】@PostConstruct & @PreDestroy使用示例

【Spring源码】AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

【Spring源码】CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

22

【Spring源码】21. 初探循环依赖

【补充内容】

【保姆级】手把手Debug循环依赖的整体流程

【实践向】当移除了三级缓存……

【分析向】没有三级缓存会导致什么?

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

23

【Spring源码】22. 属性填充populateBean()详解

【补充内容】

【Spring源码】自动注入·名称:autowireByName()详解

【Spring源码】自动注入·类型:autowireByType()详解

【Spring源码】属性值的解析与赋值:populateBean().applyPropertyValues()

【保姆级】超超超简单的自定义注解实现@Autowired同款功能

24

【Spring源码】23. 执行初始化逻辑:initializeBean()

通过上篇(【实践向】当移除了三级缓存…… )的实践,我们得出的结论是:如果不存在代理对象,二级缓存就可以解决循环依赖性的问题,但是当存在代理对象的时候,二级缓存则无法完全解决循环依赖,需要引入三级缓存

那么、在没有三级缓存的情况下,引入代理后为什么会报错?

由于我们的代码只修改了两处,但前后出现了不同执行结果,所以我们可以将分析重点放在修改的两处代码上(代码与文章【实践向】当移除了三级缓存…… 中的修改后的代码一致,想动手实践下的可以去瞅下,本文较长,这里就不贴叻)

给个“缩略图”吧(˶‾᷄ ⁻̫ ‾᷅˵)下面两张图中红框框内注释掉的为原代码,未注释的为修改后的代码

  • 第一处修改的位置

【分析向】没有三级缓存会导致什么?_第1张图片
  • 第二处修改的位置

【分析向】没有三级缓存会导致什么?_第2张图片

流程图

先放个报错的流程图,可以对照这个看后面的Debug步骤

【分析向】没有三级缓存会导致什么?_第3张图片

Start^ ^!

ps >>> 自行尝试Bebug的客官需要注意的是我们这里只关注与beanA和beanB这两个bean相关的对象的相关操作,所以如果遇到Variable中的“name”或者“beanName”不是beanA/beanB的对象就直接单击Resume进入下一个断点即可(毕竟Spring不只需要创建这俩Bean)

【分析向】没有三级缓存会导致什么?_第4张图片

启动后,获取BeanA

【分析向】没有三级缓存会导致什么?_第5张图片
【分析向】没有三级缓存会导致什么?_第6张图片

未获取到,开始创建BeanA的实例对象

【分析向】没有三级缓存会导致什么?_第7张图片

我们直接到BeanA的实例化完成后,将其半成品BeanA@2289放入二级缓存的位置(本文中的半成品均指只完成实例化未完成初始化的实例对象)

【分析向】没有三级缓存会导致什么?_第8张图片

上图可以看到此时半成品BeanA@2289的属性beanB还是null,接着开始进行属性填充,applyPropertyValues()方法中通过 valueResolver.resolveValueIfNecessary()方法解析出beanA的属性beanB并尝试从缓存中获取

【分析向】没有三级缓存会导致什么?_第9张图片

显然还是获取不到的,于是开始创建BeanB的实例对象

【分析向】没有三级缓存会导致什么?_第10张图片

实例化完成后生成了BeanB的半成品对象BeanB@2416,同样将其放入二级缓存中

【分析向】没有三级缓存会导致什么?_第11张图片

然后进行属性填充,解析并尝试获取半成品对象BeanB@2416的属性beanA,此时二级缓存中已经有了BeanA的半成品对象BeanA@2289,于是获取到了对象BeanA@2289

【分析向】没有三级缓存会导致什么?_第12张图片

通过调用setPropertyValues()方法将获取到的对象BeanA@2289填充到BeanB@2416对象的属性beanA中(如下图可以看到BeanB@2416的属性beanA已经是对象BeanA@2289了)

【分析向】没有三级缓存会导致什么?_第13张图片

继续完成对象BeanB@2416的初始化

【分析向】没有三级缓存会导致什么?_第14张图片

初始化过程中先是调用前置处理器

【分析向】没有三级缓存会导致什么?_第15张图片

这里会遍历该工厂创建的bean的BeanPostProcessors,最终返回一个经过其所有BeanPostProcessor对象的后置处理器层层包装的对象

【分析向】没有三级缓存会导致什么?_第16张图片

然后执行初始化方法,最后会调用后置处理器

【分析向】没有三级缓存会导致什么?_第17张图片

与前置处理器逻辑基本一致

【分析向】没有三级缓存会导致什么?_第18张图片

但是,有一个特别的BeanPostProcessor

【分析向】没有三级缓存会导致什么?_第19张图片

此时会进入到AbstractAutoProxyCreator抽象类的postProcessAfterInitialization()方法中

【分析向】没有三级缓存会导致什么?_第20张图片
【分析向】没有三级缓存会导致什么?_第21张图片

在wrapIfNecessary()中为BeanB创建代理对象

【分析向】没有三级缓存会导致什么?_第22张图片

将创建好的代理对象放入集合proxyTypes中,然后返回

【分析向】没有三级缓存会导致什么?_第23张图片

返回到调用后置处理器的方法中,再继续遍历完剩余的后置处理器,对新生成的代理对象进行层层包装,最后将该代理对象返回

返回到初始化方法中

值得注意的,在applyBeanPostProcessorsAfterInitialization()方法的返回值赋值给wrapBean对象前,该对象的值是BeanB@2416

【分析向】没有三级缓存会导致什么?_第24张图片

完成赋值后,wrapBean对象的值变成了BeanB@1a6d8329,没错就是刚创建的代理对象

【分析向】没有三级缓存会导致什么?_第25张图片

接着完成了将bean对象注册到容器的操作,BeanB的实例对象的创建就完成了

还记得我们为啥么要创建BeanB的实例对象吗?

自问自答:因为在给BeanA的实例对象beanA的beanB属性赋值的时候没有从缓存中获取到BeanB的实例对象,于是我们开始了BeanB的实例对象的创建,所以当BeanB的实例对象创建好后,就会继续回到BeanA对象填充beanB属性的步骤,并将返回的BeanB@1a6d8329对象通过setPropertyValues()方法填充到了的beanB属性中

【分析向】没有三级缓存会导致什么?_第26张图片

此时就完成了BeanA@2289对象的属性填充,开始执行初始化方法initializeBean()

【分析向】没有三级缓存会导致什么?_第27张图片

根据上面执行的经验,我们直接进入到后置处理器的调用方法applyBeanPostProcessorsAfterInitialization()中

【分析向】没有三级缓存会导致什么?_第28张图片

可以看到此时需要遍历的有4个BeanPostProcessor,当遍历到AspectJAwareAdvisorAutoProxyCreator时

【分析向】没有三级缓存会导致什么?_第29张图片

进入postProcessAfterInitialization()方法,会发现再次来到了AbstractAutoProxyCreator抽象中

【分析向】没有三级缓存会导致什么?_第30张图片

于是相同的流程,进入wrapIfNecessary()方法,一步一步创建BeanA的代理对象

创建完成后,将返回的代理对象BeanA@1c7696c6赋值给exposedObject对象,而这个对象原先的值是BeanA@2289

【分析向】没有三级缓存会导致什么?_第31张图片

然后就要开始咱们的终极大判断叻(。・ω・。)ノ

【分析向】没有三级缓存会导致什么?_第32张图片

获取缓存中名为beanA的对象并赋值给earlySingletonReference,此时earlySingletonReference是BeanA@2289,exposedObject在刚刚的操作被赋值为BeanA@1c7696c6,因此和bean(BeanA@2289)并不相等,于是程序会继续判断!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName),两个判断都满足,于是进入到else if的代码块中

在这段代码中获取到了beanA的依赖对象有beanB,并将其加入到集合actualDependentBeans中

【分析向】没有三级缓存会导致什么?_第33张图片

End= =

因此此时!actualDependentBeans.isEmpty()的值为true,于是进入到了if条件代码块中,嘿嘿、然后就报错了(˶‾᷄ ⁻̫ ‾᷅˵)……

【分析向】没有三级缓存会导致什么?_第34张图片

引发的问题

没有三级缓存引发了什么问题?

通过上面的Debug流程和报错信息不难发现,移除了三级缓存使得在容器中存在BeanA对象的实例,一个是已经注入到BeanB的半成品对象BeanA@2289,另一个是执行初始化时生成的代理对象BeanA@1c7696c6,一个单例模式下的容器怎么能容纳两个名为beanA对象,因而报错

简单来说,就是在生成代理对象的时候,发现已经有一个同名的对象存在并被其他对象依赖了

这个问题是在哪里解决的?

回答放前面(体贴下不想看过程的朋友(。・ω・。)ノ)

这个问题自然是在上面测试流程中修改掉的两个方法getSingleton()和addSingletonFactory()中处理解决的

其实无论是否配置了代理,二级缓存中都是存放半成品对象的

三级缓存的存在使得当判断需要生成代理对象时,会从从三级缓存中直接调用工厂对象生成代理对象,并添加进二级缓存中

恢复原先的代码

再次启动,我们直接从BeanA的实例对象的创建开始,可以看到下图中执行到我们一开始修改代码用二级缓存替换三级缓存的位置,此时,二级缓存和三级缓存都还是空的(黄框框),并且已经创建了一个BeanA的半成品对象BeanA@2324

【分析向】没有三级缓存会导致什么?_第35张图片

然后我们进入方法addSingletonFactory()中

【分析向】没有三级缓存会导致什么?_第36张图片

执行完以后,就将beanA和其lambda表达式添加到了三级缓存中,此时二级缓存依旧是空的

【分析向】没有三级缓存会导致什么?_第37张图片

随后将半成品对象BeanA@2324赋值给exposedObject,继续执行下面的逻辑

【分析向】没有三级缓存会导致什么?_第38张图片

值得注意的是,这次,这个半成品对象并没有放进二级缓存中,那么意味着在后面创建BeanB的实例对象进行属性注入时,无法从二级缓存中获取到这个半成品对象,那么按照源码的逻辑,会继续尝试从三级缓存中获取,于是,会获取到在addSingletonFactory()方法中添加的beanA的lambda表达式,进一步创建出代理对象,为BeanB的实例对象的beanA属性赋值

我们继续Debug到创建BeanB的实例对象进行属性注入时获取BeanA的步骤

【分析向】没有三级缓存会导致什么?_第39张图片

由于此时一级缓存、二级缓存都为空,因此会尝试从三级缓存中获取,此处会通过singletonFactory.getObject()这个回调方法去调用getEarlyBeanReference()方法(原因如下图),进而开始BeanA代理对象的创建

【分析向】没有三级缓存会导致什么?_第40张图片

在刚进入这个方法时,可以看到bean对象和exposedObject对象的值是一致的,都是BeanA@2324

【分析向】没有三级缓存会导致什么?_第41张图片

当这个方法中的逻辑都执行完成后,bean对象还是BeanA@2324,而exposedObject已经变成了代理对象BeanA@60099951

【分析向】没有三级缓存会导致什么?_第42张图片

回调方法执行完后,将生成的代理对象返回赋值给变量singletonObject,接着将这个生成的代理对象添加进二级缓存,并从三级缓存中移除

【分析向】没有三级缓存会导致什么?_第43张图片

使用获取到的代理对象完成属性赋值

【分析向】没有三级缓存会导致什么?_第44张图片

继续完成BeanB实例对象的初始化,与BeanA的初始化流程一致,调用后置处理器的过程中同样会为BeanB生成代理对象,如下图中再在遍历调用AspectJAwareAdvisorAutoProxyCreator的postProcessAfterInitialization()初始化后置方法前,result=BeanB@2450

【分析向】没有三级缓存会导致什么?_第45张图片

遍历结束后,返回的值是新生成的BeanB的代理对象BeanB@20140db9

【分析向】没有三级缓存会导致什么?_第46张图片

在获取到完整的BeanB实例对象(代理对象)后,回到BeanA创建对象属性赋值的流程中,将获取到的代理对象BeanB@20140db9赋值给BeanA的beanB属性

【分析向】没有三级缓存会导致什么?_第47张图片

继续执行BeanA的实例对象的初始化流程,来到调用后置处理器的步骤(嘿嘿( ̄∇ ̄)没错这里又要创建代理对象叻)

【分析向】没有三级缓存会导致什么?_第48张图片

一切都是那么相似,又要调用AspectJAwareAdvisorAutoProxyCreator的postProcessAfterInitialization()初始化后置方法,来获取BeanA的代理对象,不同的是,此时二级缓存(earlySingletonObjects)中已经有了一个之前创建好的BeanA的代理对象BeanA@60099951,所以不会再创建了,直接返回传入的对象BeanA@2324

接下来的判断earlySingletonExposure为true,于是从缓存中获取到BeanA的代理对象,赋值给earlySingletonReference

由于初始化函数返回的exposedObject和bean对象相等,所以开始执行if条件代码块,即将earlySingletonReference的值赋值给exposedObject,并返回exposedObject

于是获取到了BeanA的代理对象BeanA@60099951

【分析向】没有三级缓存会导致什么?_第49张图片

此时这个代理对象的属性beanB还是null

【分析向】没有三级缓存会导致什么?_第50张图片

而通过beanA.getBeanB()获取的BeanB对象(进入了拦截器的逻辑),其beanA属性也是null(这跟没有引入代理的循环依赖也有区别)

【分析向】没有三级缓存会导致什么?_第51张图片

当移除代理的相关配置后

【分析向】没有三级缓存会导致什么?_第52张图片

还记得代理是在哪一个步骤替换掉同名的一般对象的吗?

自问自答:getEarlyBeanReference()

而当我们不引入代理时

【分析向】没有三级缓存会导致什么?_第53张图片

此时,不会进入红框框内的判断逻辑代码块

返回的exposedObject对象就是传入的bean对象(都是BeanA@1513)

【分析向】没有三级缓存会导致什么?_第54张图片

接着会将获取到的BeanA的半成品对象放入二级缓存,并将其工厂方法从三级缓存中移除

【分析向】没有三级缓存会导致什么?_第55张图片

接着BeanB继续执行属性赋值、初始化,并在addSingleton()方法中,将生成的完整对象BeanB@1554放入一级缓存,将他的工厂方法从三级缓存中移除

【分析向】没有三级缓存会导致什么?_第56张图片

可以看到初始化步骤中调用的后置处理器也从引入代理时的4个,减少到了3个,所以此处不会生成代理对象来覆盖已经生成的一般对象

【分析向】没有三级缓存会导致什么?_第57张图片

此时全局只有一个BeanA的实例对象BeanA@1513

【分析向】没有三级缓存会导致什么?_第58张图片

自然也没有机会报红框框中的异常(。・ω・。)ノ

【分析向】没有三级缓存会导致什么?_第59张图片

一路返回,在addSingleton()方法中,将BeanA@1513也加入了一级缓存,并从二级三级缓存中把同名对象移除

【分析向】没有三级缓存会导致什么?_第60张图片

于是一开始进入的getSingleton()方法获取了BeanA的完整的实例对象

【分析向】没有三级缓存会导致什么?_第61张图片

一路返回到最初的getBean

【分析向】没有三级缓存会导致什么?_第62张图片

于是循环依赖也解决了٩(˃̶͈̀௰˂̶͈́)و

你可能感兴趣的:(Java,java,Spring,源码,三级缓存,循环依赖)