SpringBoot中三级缓存及循环依赖的解决

三级缓存的概念是针对单例bean来说的,原型的bean是每次直接创建新的bean对象,

IoC容器中,同一个bean或者其代理对象只能存在一个,不能同时存在

 

一级缓存

存放完整的bean(实例化和初始化都已完成)

二级缓存

提前曝光的不完整的bean(可能是bean,也可能是bean的AOP代理,未进行初始化) 

作用:防止创建出多个相同的bean(或者AOP代理)

三级缓存

存放ObjectFactory,这是一个Lambda表达式,执行这个表达式会拿到bean或者bean的代理,如果不发生循环依赖的话,三级缓存的表达式是不会被调用的

作用:解决AOP问题,因为这个表达式返回的是bean本身或者AOP代理过的bean

 

 

Spring容器启动,扫描代码,拿到很多需要单例的bean,严格根据字母顺序进行实例化

bean在实例化以后都会把自己的lambda接口放入三级缓存,这个lambda的作用是返回bean本身或者返回bean的代理,这个lambda不一定会被执行,仅在解决循环依赖的时候才会被执行

正常情况下,Spring是根据bean的生命周期来创建bean并放入IoC,但是遇到循环依赖的话,会通过三级缓存、提前曝光、提前进行AOP代理 来解决循环依赖(就不是严格按照bean的生命周期了,因为循环依赖的情况很少)

这里最好回顾一下bean的生命周期

 

根据需要从三级缓存取出来的ObjectFactory执行以后会得到的不完整的(可能是AOP代理)bean会放到二级缓存,并移除在三级缓存中的内容

 

问: 只有二级缓存能不能解决循环依赖:

答:如果不遵守bean的生命周期,把AOP代理提前到实例化之后就做代理,把bean或者AOP代理bean放入二级缓存,那是可以的

 原型bean无法解决循环依赖,能解决的都是单例模式。

 另外可以使用@Lazy注解来进行延迟加载解决构造器注入的循环依赖,即等到用到才通过动态代理来创建

 

 

如果没有AOP代理的话,不完整的bean直接放入二级缓存,完整的bean放入一级缓存

 

以下举个循环依赖的初始化例子

这里假设有2个bean,A、B, AB相互依赖

1. Spring容器启动,扫描代码,先找到一个需要变成单例的A

2. bean A的生命周期开始

3. 进行实例化A, 把A放到三级缓存,key是A的name,value是一个lamba(ObjectFactory)

4. 设置属性,发现依赖B,依次从一级、二级、三级缓存去找B,没找到,进入B的生命周期

5. bean B的生命周期开始

6. 进行实例化B, 把B放到三级缓存(同上)

7. 设置属性,发现依赖A,依次从一级、二级、三级缓存去找A,找到了,执行A的lambda得到不完整的bean A(可能是AOP代理),把bean A放入二级缓存,并在三级缓存中移除

8. 执行B的剩余生命周期

9. A继续设置属性B,执行A的剩余生命周期

10. 继续扫描初始化需要的单例

 

 

构造环境:

Test1和Test2这2个bean循环依赖

加上Transaction注解,就会让Spring给这两个bean加上代理

properties里面打开允许循环依赖的开关

 

 

 

当需要设置属性是某个bean的时候就会去调用getSingleton方法,

这个方法是依次从一级缓存、二级缓存、三级缓存中找这个bean

 

 

执行ObjectFactory的getObject就是调用了getEarlyBeanReference,

里面又调用了wrapIfNecessary,得到了bean本身或者bean的代理

 

 

参考

https://blog.csdn.net/mweibiao/article/details/126133411

posted @ 2024-03-20 20:26  坏男银  阅读(646)  评论(0编辑  收藏  举报