自学内容网 自学内容网

Spring的三级缓存机制

1. 前言

        在学习SSM课程时,针对Spring三级缓存机制进行的画图和记录。感谢雷老师的精彩讲解。网上有很多通过源码级别讲解三级缓存的文章,此篇博客只是记录学习时画的一些流程图和自己的一些理解。

2. 核心流程

2.1. BeanFactory(Bean工厂)

2.2 三级缓存解决循环依赖

2.2.1. 三级缓存的定义

Spring的三级缓存实际上是三级Map,用户管理单实例(Singleton Beans)的初始化:

(1)一级缓存:(singletonObjects) 用于存储完全初始化完成的Bean。

(2)二级缓存:(earlySingletonObjects)用于存储原始对象 "提前暴露的对象” (已创建但未完成依赖注入的Bean)。用于解决循环依赖。

(3)三级缓存:(singletonFactories)用于存储Bean工厂(ObjectFactory),用于生成实例的代理对象。

2.2.2. 解决循环依赖的流程

        以A依赖B,B依赖A为例说明三级缓存解决循环依赖的流程。循环依赖关系如下图:

1.创建A的原始实例

  • Spring首先创建A的原始实例(通过反射调用构造函数),但此时A还未完成属性填充和初始化步骤。
  • A的原始实例会存入第三级缓存(singletonFactories),用于暴露代理或原始对象。

2. 填充A的依赖B

  • 在处理A的依赖时,Spring发现A依赖B,于是递归创建B。

3. 创建B的原始实例

  • 类似地,Spring会通过反射调用构造函数创建B的原始实例,放入三级缓存,暂时还未完成初始化。

4. 填充B的依赖A

  • 在处理B的依赖时,Spring发现B依赖于A。
  • Spring会先尝试从一级缓存(singletonObjects)中获取A,但此时A尚未完全初始化,因此不在一级缓存中。
  • 接下来,Spring尝试从二级缓存(earlySingletonObjects)中获取A的提前暴露对象。
  • 如果二级缓存中也没有,Spring会从三级缓存中调用工厂方法,获取A的提前暴露对象,并将其放入二级缓存中。

5. 完成B的初始化

  • B获取到A的提前暴露对象后,可以完成自身的依赖注入和初始化操作。
  • B初始化完成后,B被移入一级缓存(singletonObjects)中,并从三级缓存和二级缓存中移除。

6. 完成A的初始化

  • 返回到A的初始化过程,此时B已经完成初始化(并且在一级缓存中了),所以可以正常注入到A中。
  • A的依赖注入和初始化完成后,A被移入一级缓存中,并从二级缓存和三级缓存中移除。

上述数据流示意图如下:

2.2.3. 三级缓存的优势和不适用的场景

核心优势:

(1)解决循环依赖:通过提前暴露Bean的引用,使得相互依赖的Bean可以在初始化过程中被访问;

(2)性能优化:将完全初始化的Bean存储在一级缓存中,减少反复初始化;

(3)灵活性:通过三级缓存的延迟创建,可以对Bean进行动态代理或扩展。

不能解决的场景:

        Spring的三级缓存默认是解决单实例Bean的循环依赖。如果互相依赖的Bean是通过构造方法进行注入的,三级缓存没法解决。 

2.2.4. 总结

        Spring通过三级缓存机制解决了容器中组件的循环依赖问题,核心是第三级缓存(singletonFactories)中存放的是创建Bean组件的工厂方法,它允许在 Bean 初始化完成之前提前暴露其对象引用。如果A依赖B,B依赖A,其核心流程就是:

        对A进行依赖注入B时,会通过工厂方法从三级缓存中获取A的提前暴露的对象,并放入二级缓存供B使用,待B完成初始化后,B会被移入一级缓存中,并从二级和三级缓存中移除。此时A就可以正常注入B,待A完成初始化后会被移入一级缓存并从二级缓存和三级缓存中移除。从而避免了循环依赖导致的死锁问题。

3. 如果此篇文章对你有帮助,感谢点个赞~


原文地址:https://blog.csdn.net/qq_23388169/article/details/145179202

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!