Spring 三级缓存

【白话Spring三级缓存 容器只是个工具,怎么用才是我们需要了解的关键,什么时候给容器里存入Bean,把Bean存入哪个容器,什么时候清空容器,这就不得不了解一下Spring中Bean的生命周期了。设想一下没有三级缓存的循环依赖问题:ServiceA依赖ServiceB,ServiceB依赖ServiceA。每次获取到的都是新的代理对象,就破坏了Spring解决循环依赖问题的基础,即所有的对象都是单例的。解决循环依赖的问题有两级缓存就够了,为什么要用三级缓存呢?从上面的代码中可以看出,三级缓存就是3个全局的Map。 阅读详情

一、序言

本文介绍 Spring 中如何使用三级缓存解决循环依赖问题的。

二、什么是 Spring 循环依赖

image.png
在 Spring 中,循环依赖是指多个 Bean 之间相互依赖,形成了一个闭环。例如上图:

  1. Bean A 若依赖自身,形成循环依赖
  2. Bean A 依赖 Bean B,Bean B 又依赖 Bean A,形成了循环依赖
  3. Bean A 依赖 Bean B,Bean B 依赖 Bean C,而 Bean C 依赖 Bean A,形成了循环依赖

我们接下来以第二个图的循环依赖为例展开讨论(即 A 依赖 B,B 又依赖 A)。

三、Spring 三级缓存处理流程分析

在这里插入图片描述

Spring 创建 Bean A 与 Bean B 的流程如下:

  1. 从三级缓存中获取 Bean A
  2. 获取成功,直接返回
  3. 获取失败,开始创建 Bean A
  4. 实例化 Bean A
  5. 将实例化之后的 Bean A 放入三级缓存
  6. Bean A 准备填充属性 Bean B
  7. 从三级缓存中查找 Bean B
  8. Bean B 存在,Bean A 属性填充成功,Bean A 初始化成功
  9. Bean B 不存在,开始创建 Bean B
  10. 实例化 Bean B
  11. 将实例化之后的 Bean B 放入三级缓存
  12. Bean B 准备填充属性 Bean A
  13. 从三级缓存中查找 Bean A(一定能找到,因为之前已将 Bean A 放入了三级缓存)
  14. Bean B 属性填充成功,Bean B 初始化成功
  15. Bean A 继续执行填充属性的步骤
  16. Bean A 属性填充成功(Bean B 已存在),Bean A 初始化成功

至此,流程分析结束。

四、为什么是三级缓存

我们将三级缓存执行流程图中的三级缓存改成一级缓存(即只有一层缓存)

在这里插入图片描述

该逻辑流程似乎依旧能够解决循环依赖。没错,其实只需要一级缓存便可以解决循环依赖。那么,其余两层缓存的作用究竟是什么呢?

4.1 循环依赖中的 AOP

@Component
public class A {
    // 注入 B
    @Autowired
    private B b;
}

@Component
public Class B {
    // 注入 A
    @Autowired
    private A a;
}

// A 的切面类
@Aspect
public class AAspect {

    @Around("execution(* A(..))")
    public Object handle() {}
}

思考一下,如果上述三个类注入到 Spring 中,Spring 该如何去创建 Bean?
如果按照我们之前的流程:Bean A 会先放入缓存,接着 Bean B 创建,Bean B 发现依赖 Bean A。此时,Bean B 会去缓存中寻找 Bean A。
在上述的代码中,由于 Bean A 有一个切面,所以理应是去缓存中寻找 Bean A 的代理。如果只有一级缓存,该如何解决这个问题呢?

4.2 三级缓存解决方案

针对 AOP 的循环依赖问题,Spring 给出的是三级缓存方案。这个方案的具体内容是:

在这里插入图片描述

  1. Bean A 实例化,向三级缓存中放入其 ObjectFactory 包装的对象
  2. Bean A 填充属性 Bean B,Bean B 在缓存中未找到
  3. Bean B 实例化,向三级缓存中放入其 ObjectFactory 包装的对象
  4. Bean B 填充属性 Bean A,发现三级缓存中有 Bean A,调用 getObject() 获取对象
  5. Bean A 若有代理对象,则返回的是代理对象;反之,返回原始 Bean A
  6. 半成品的代理/原始 Bean A 放入二级缓存,移除三级缓存中的内容
  7. 初始化 Bean B 完成
  8. 完整 Bean B 放入一级缓存,移除三级缓存 Bean B 的 ObjectFactory
  9. 初始化 Bean A 完成
  10. 完整 Bean A 放入一级缓存,移除二级缓存中代理/原始的 Bean A

两个问题:

  1. 为什么使用 ObjectFactory 包装?

    之所以使用 ObjectFactory 去包装原始的 Bean 对象,这是因为 Bean 可能会有代理对象(例如:上文中 Bean A 存在切面)。究竟是获取代理 Bean 还是原始 Bean,可以统一让 ObjectFactory 处理。

  2. 为什么 ObjectFactory#getObject() 方法获取的代理/原始 Bean 对象不直接放入一级缓存/三级缓存?

    ObjectFactory#getObject() 获取的代理/原始 Bean 是一个半成品的对象(属于中间的一种状态),而一级缓存放的是完整的对象,三级缓存中放的是获取对象的 Factory。无论是从设计还是从逻辑上去考量都应该将这种状态单独处理。

AI工程实战:如何在实际工程中运用Claude Code 本文探讨了利用大型语言模型(LLM)进行复杂工程项目的结构化工作流。核心策略包括:1)规划先行,通过"探索子代理任务"主动分析代码库并澄清需求;2)定制AI行为,要求简洁输出和列出未解决问题;3)将大型任务分解为多阶段计划以管理上下文窗口限制;4)利用GitHub Issues作为外部存储实现跨会话状态持久化。这种"规划-执行"模式将AI从代码生成器升级为项目管理代理,通过与GitHub CLI等工具深度集成,实现了高效的多阶段工程协作。该方法展示了AI辅助编程向流程 阅读详情

相关推荐

RK3588开发笔记-MIPI-CSI接口视频解码芯片XS9922B调试

Rockchip RK3588 是一款高性能的处理器,支持多种摄像头接口,包括 MIPI(Mobile Industry Processor Interface)接口。MIPI是一种用于移动设备和嵌入式系统中的摄像头、显示器和其他传感器设备的串行接口标准。MIPI是MIPI联盟发起的为移动应用处理器制定的开放标准。CSI-2(Camera Serial Interface 2)是MIPI 联盟定义的另一种高速接口,主要用于摄像模组和处理器之间的连接。

flypig has a dream! 6765

spring三级缓存的流程

我们直接进入finishBeanFactoryInitialization的最后一个方法 就是从这里 spring开始了创建对象的流程 接下来 我们进入getBean方法中观察它实现了什么 首先调用doGetBean方法 继续进入 我们见到了一个非常重要的方法 Object sharedInstance = getSingleton(beanName); 它的作用是 从容器中获取bean对象 我们看看它里面是干什么的 我们从缓存中并没有获取到对象 所以下一步 我们需要创建对象 下面的很多

m0_49708063的博客 1518

【Unity URP渲染管线下Shader的自定义物体半透明和投影半透明效果_半透明案例分享】

Unity Shader Graph URP渲染管线下的自定义半透明效果_半透明案例分享

暴走约伯的博客 3269

Spring三级缓存

一级缓存(Singleton Objects)存储完全初始化好的bean,即依赖注入完全完成,可以直接使用的bean。二级缓存(Early Singleton Objects)存储原始的bean(尚未完成依赖注入),通常是在bean实例化之后,但在依赖注入开始之前。三级缓存(Singleton Factories)存储bean工厂对象,用于生成对应的bean,通常会包含一个引用到,通过这个工厂对象可以获取早期的bean引用。

qq_43012298的博客 1886

Spring Bean的生命周期及三级缓存流程

Spring Bean 的生命周期说起来其实就三个大块:实例化Bean -> 设置属性(注入和装配) -> 初始化 -> 使用Bean -> 销毁Bean这个很好理解,但是内部是怎么样注入,初始化以及销毁?经历怎么样的过程呢?追随这些问题来参考这篇文章。我们先从 三级缓存 -> 实例化... 顺序逐步理解。

weixin_63426509的博客 1657

Spring三级缓存流程再梳理

本文主要是说下在使用spring时遇到了循环依赖,Spring利用三级缓存怎么解决备注:singletonFactory.getObject和Bean的初始化都会经过AOP来判断一次是否需要创建代理对象,但是二者只会有一个创建代理对象,他们里面利用了一个中间earlyProxyReferences缓存,创建过就不会再创建了,这个缓存不是三级缓存别搞混了。

weixin_37862824的博客 452

图解Spring三级缓存的工作流程

4. 把不完整(只初始化了,属性并没有填充) 目标Bean注入到目标Bean的子Bean中(我艹,好绕)三级缓存是为了解决循环依赖注入的问题,而且是单例的情况,其他情况是用不到三级缓存的。3. 再从三级缓存找到目标Bean移动到二级缓存,再从三级缓存中移除。2. 再实例化目标Bean的子Bean,并依赖注入, 放到三级缓存。6. 把目标Bean的子Bean注入到目标Bean。8. 目标Bean完成了属性填充,放入一级缓存。1. 先实例化目标Bean,放到三级缓存。7. 从一级缓存找到子Bean的实例。

lee576的专栏 963

Spring三级缓存

初始化对象 是否可以没有二级缓存 是否需要三级缓存 主要参考以下几篇文章做了简单的总结:一文告诉你Spring是如何利用"三级缓存"巧妙解决Bean的循环依赖问题的Spring循环依赖三级缓存是否可以去掉第三级缓存spring:我是如何解决循环依赖的?Spring 解决循环依赖为什么使用三级缓存,而不是二级缓存...

m0_67401417的博客 2万+

Spring三级缓存详解

Spring三级缓存是为了解决对象间的循环依赖问题。

WorldMvp的专栏 1万+

详解Spring三级缓存

详解Spring三级缓存

. 1万+

Spring三级缓存机制

这时,bean B 也需要依赖 bean A,但由于 bean A 的工厂对象已经在三级缓存中,Spring 可以直接从三级缓存中获取到 bean A 的工厂对象,并通过它来创建 bean A 的实例。而对于需要被AOP代理的Bean,singletonFactories中的工厂对象会先创建一个原始的Bean实例,然后通过AOP代理来生成代理对象,并将代理对象放入一级缓存singletonObjects中。:在Spring中,AOP代理是在Bean的初始化阶段完成的,这意味着所有的属性填充和方法调用之后。

m0_51431003的博客 6138

Spring三级缓存及其原因

首先看下Spring创建一个bean的基本流程: 创建该实例的原始对象 --> 进行自动装配 --> AOP代理处理 --> 完成bean的创建并加入单例池(即一级缓存) 但是当有循环依赖的时候,Spring是如何解决的呢? 主要原理是利用三级缓存机制: singletonObjects: 一级缓存,也就是我们平常理解的单例池。 singletonFactories: 二级缓存,存储的是单例工厂。 earlySingletonObjects: 三级缓存,存储正在创建中的单例对象。

Mr_Gu的博客 9102

spring三级缓存

如果有 AOP 代理,这里会返回代理对象。,Spring 会先去二级缓存。(特别是 AOP 代理场景)。创建实例,并存入二级缓存。,提升 Bean 复用率。,最终可用的单例对象。,用于最终获取实例。

m0_66572887的博客 1825
上一篇: RBAC 权限设计(四)
下一篇: 线程池详解
知行小栈
博客等级 码龄9年 2506粉丝 153原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值