Spring中二级缓存就可以解决循环依赖?三级缓存打酱油的吗???

812 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第32天,点击查看活动详情

1. Spring中的三级缓存是什么

三级缓存,三个Map

企业微信截图_16563367015349.png

  • 第一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象
  • 第二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)
  • 第三级缓存: Map> singletonFactories,存放可以生成Bean的工厂

三级缓存是用于存放原始的注入对象,这些对象还是简单对象并没有被代理,三级缓存是用于解决循环依赖问题,当对象A依赖对象B时,A对象就会被先临时存放在三级缓存中,然后初始化B对象。在这个时间点如果有别的注入对象需要依赖A对象就会从三级缓存中查询,并通过三级缓存的getObject方法生成代理对象然后将A对象从三级缓存中删除,放入二级缓存。

2.引入二级缓存就可以打破循环依赖

其实引入二级缓存之后,就有专门存放半成品的缓存,就不会陷入闭环的尴尬,那么为什么要引入三级缓存呢?

3.引入三级缓存(解决A依赖B,B依赖A的代理对象问题)

当我们手动改变源代码,把需要三级缓存的地方去掉,看是否影响对象的创建?

没有三级缓存,对ab对象的循环依赖没啥影响,但是为什么还要使用三级缓存呢?

当添加 AOP 之后,抛出异常,显示没有使用最终版本的bean对象。

Lambda表达式:()->getEarlyBeanReference(beanName,mbd,bean)

总结:当被代理的对象之间存在循环依赖问题的时候,需要使用三级缓存,如果没有代理对象的产生,只需要二级缓存即可解决问题。

在 Spring 容器中,同名的 bean 只有一个,

在创建对象的过程中,不知道对象什么时候会被代理,但是在创建对象的时候,要遵循标准的bean创建的流程,也就是无论当前对象是否需要被代理,总会提前生成一个普通对象

如果执行过程中发现需要创建代理对象怎么办?需要使用代理对象将原来的普通对象给覆盖掉

因为不知道当前对象什么时候会被引用,所以只能在第一次引用的时候来判断对象是否需要被代理,所以通过 Lambda 表达式的方式进行回调,第一次需要的时候来判断是否需要被覆盖。

因为如果直接在二级缓存中创建好半成品对象之后,当被引用的时候,发现需要创建代理对象,此时会返回一个新的代理对象,而代理对象和之前二级缓存中的对象不是同一个对象

比如A依赖B,B依赖A的代理。

使用三级缓存,在需要 AOP 的时候,直接创建一个代理对象,然后直接将代理对象放到二级缓存中即可,以后该对象不会再变化,因此当使用 AOP 的时候需要使用三级缓存。

4. 三级缓存解决代理对象的循环依赖

4.1.对象产生的过程

创建对象的时候,主要会经历四个方法,分别是:getSingleton,doCreateBean,populateBean,addSingleton

  • getSingleton:希望从容器里面获得单例的bean,没有的话执行方法2
  • doCreateBean: 没有就创建bean
  • populateBean: 创建完了以后,要填充属性
  • addSingleton: 填充完了以后,再添加到容器进行使用

4.2.A依赖B,B依赖A的代理对象

A依赖B,B依赖A的代理对象过程如下:

getBean 在容器(包括一二三级缓存)中获得A对象,没有则创建A对象;

初始化A对象,将A对象的 ObjectFactory 的 lamda 放入三级缓存 (此时并没有生成A的原始对象,而只是在工厂中)

A中依赖了B,填充属性B对象;

getBean 在容器(包括一二三级缓存)中获得B对象,没有则创建B对象;

初始化B对象,将B对象的 ObjectFactory 的lambda放入三级缓存;

B中依赖了A的代理对象,填充属性A对象,在三级缓存中找到A对象(正在创建中的A对象);

调用A的 ObjectFactory,生成代理对象,创建半成品,将创建好的半成品放到二级缓存,并将三级缓存中的A移除; (此时才正在生成A的对象,生成的是A的代理对象,保重了在Spring中A对象自始至终只有一个,且A对象的地址值没有发生变化)

取到 A 对象之后,给B对象中的 A 属性赋值,那么此时 B 对象则创建完成,放到一级缓存,三级缓存删除B;

此时再给 A 对象中的 B 对象赋值,将成品 A 放到一级缓存,将二级缓存中的 A 删除;

此时 A 对象创建完成,再创建 B 对象,但是在容器中直接拿到了 B 对象,则无需再次创建,结束。