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。无论是从设计还是从逻辑上去考量都应该将这种状态单独处理。

【WorkBuddy专栏09】WorkBuddy多模态能力深度揭秘 【WorkBuddy专栏09】AI不止会聊天——WorkBuddy多模态能力深度揭秘 阅读详情

相关推荐

集成calibre到cadence virtuoso环境中 load: can‘t access file - “/opt/eda/Mentor/lib/calibre.skl“

项目场景: 设想是将Calibre集成到cadence virtuoso中,但网上的解决办法尝试过在本机上实验都有点问题,因此想记录下来我的解决办法仅供参考。 问题描述: 安装好cadence, calibre之后打开virtuoso,CIW窗口显示 *Error* load: can't access file - "/opt/eda/Mentor/lib/calibre.skl" 问题是出在calibre.skl的加载路径出错,尝试网上的在工程目录下修改.cdsinit文件,添加calibre安装

weixin_52714276的博客 1万+

spring三级缓存的流程

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

m0_49708063的博客 1520

Altera下载器驱动配置:手把手教程(从零实现)

详解Altera USB-Blaster驱动安装全过程,从设备识别到软件配置一步一图,解决常见连接问题,确保开发下载器正常工作,适合初学者快速上手FPGA下载调试。

weixin_35751194的博客 986

Spring三级缓存

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

qq_43012298的博客 1891

VS2019+CAXACAD2023二次开发教程(一、环境搭建)

CAXACAD2023的二次开发相关文件和库都在的文件夹下。CAXACAD2023的默认开发环境是VS2019,如果是用VS2019的环境话,可以直接安装这个插件,安装好后就可以一键新建的项目,新建的项目会自动帮我们配置好所有的环境,并且生成必要的代码和初始的框架。(本来想用VS2022的,但配置环境太麻烦,用VS2022,增加一个环境变量是可以运行自带的例程的,但新建项目要配置的东西和环境太多,就放弃折腾了)

weixin_42968757的博客 4613

VTK vtkLookupTable vtkScalarBarActor 颜色条

有时需要给数据做一个 vtkScalarBarActor, 我们把显示的数据的一个 坐标系 来显示 颜色条1.以 Z 显示颜色 效果如下。

q610098308的专栏 4033

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

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

weixin_63426509的博客 1660

OpenCV实例(一)人脸检测

计算机视觉使很多任务成为现实,其中两项任务就是人脸检测(在图像中定位人脸)和人脸识别(将人脸识别为特定的人)。OpenCV实现了一些人脸检测和识别的算法。从安全到娱乐,这些技术在现实环境中都有应用。介绍OpenCV的一些人脸检测和识别功能,并定义特定类型的可跟踪物体的数据文件。具体来说,将研究Haar级联分类器,通过分析相邻图像区域之间的对比度,确定给定图像或子图像是否与已知类型匹配。

小幽余生不加糖 5003

Spring三级缓存流程再梳理

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

weixin_37862824的博客 452

ThingsBoard PE专业版解决方案技术文档——温度&湿度

温湿度解决方案

weixin_67440240的博客 817

Puchem化合物数据批量抓取采集_宝典_教程_Python爬虫

本文共计6500字,导出到PDF达42页(含代码)。从0教你如何抓取Pubchem化合物数据。 1、 Pubchem介绍 ​ Pubchem是美国国立卫生研究院(NIH)的开放式化学数据库,自2004年推出以来,pubchem已成为科学家、学生和公众的重要化学信息资源。其专门针对化学结构搜索,是网络药理学研究经常用到的数据库之一,不仅使用方便而且免费。 ​ PubChem主要包含小分子,但也包含较大的分子,例如核苷酸,碳水化合物,脂质,肽和化学修饰的大分子。收集有关化学结构,标识符,化学和物理性..

qq_34843422的博客 6073

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

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

lee576的专栏 963

CloudCompare 二次开发(6)——插件中拖拽添加Qt窗口(区域生长算法为例)

手动拖拽的方式搭建Qt对话框界面的制作流程,以PCL中的点云区域生长算法为例进行制作。

点云侠的博客 2151

Spring的三级缓存

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

m0_67401417的博客 2万+

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Spring三级缓存详解

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

WorldMvp的专栏 1万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

详解Spring三级缓存

详解Spring三级缓存。

. 1万+

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Spring 的三级缓存机制

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

m0_51431003的博客 6147

Spring的三级缓存及其原因

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

Mr_Gu的博客 9108

spring三级缓存

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

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值