展开菜单
首页 精品内容 本月促销 装机必备 Windows macOS软件 IOS软件 Android AI PDF教程 专题
全部分类

当前位置:

首页 > 编程开发 > Spring三级缓存解析:循环依赖解决机制

Spring三级缓存解析:循环依赖解决机制

Spring通过三级缓存机制解决单例Bean的循环依赖问题,其中一级缓存(singletonObjects)存放完全初始化的Bean实例,二级缓存(earlySingletonObjects)存放早期引用的Bean实例,三级缓存(singletonFactories)存放用于生成早期引用的ObjectFactory;当BeanA创建过程中依赖BeanB,而BeanB又依赖BeanA时,Spring会将A的ObjectFactory放入三级缓存,B在需要A时通过该工厂获取A的早期引用(可能是代理对象),并将

Spring通过三级缓存机制解决单例Bean的循环依赖问题,其中一级缓存(singletonObjects)存放完全初始化的Bean实例,二级缓存(earlySingletonObjects)存放早期引用的Bean实例,三级缓存(singletonFactories)存放用于生成早期引用的ObjectFactory;当Bean A创建过程中依赖Bean B,而Bean B又依赖Bean A时,Spring会将A的ObjectFactory放入三级缓存,B在需要A时通过该工厂获取A的早期引用(可能是代理对象),并将其放入二级缓存供B使用,待B初始化完成后,A继续初始化并最终将完整实例放入一级缓存,从而打破循环依赖;之所以需要三级缓存而非二级,是因为在涉及AOP代理时,若直接将原始对象放入二级缓存会导致B持有的是未代理的实例,而三级缓存的ObjectFactory能按需生成代理后的早期引用,确保AOP功能正确执行;Spring通过singletonsCurrentlyInCreation标识当前正在创建的Bean,当发现某Bean正在创建中又被请求时,即触发从三级缓存获取ObjectFactory并生成早期引用的机制,从而精准判断并解决循环依赖。

Spring三级缓存详解之循环依赖解决_Java理解Spring框架的底层机制

Spring框架通过其精妙的三级缓存机制,巧妙且高效地解决了单例Bean之间的循环依赖问题。它并非简单地依靠一次性创建,而是在Bean的生命周期不同阶段,通过暴露不同状态的Bean实例,最终确保所有相互依赖的Bean都能被正确初始化并注入,避免了传统方式下因依赖无法满足而导致的创建死锁。这种设计是Spring底层机制中一个非常值得深入理解的亮点。

解决方案

Spring解决循环依赖的核心在于其三级缓存的设计与协同工作。这个过程可以概括为:当一个Bean A开始创建,并被实例化后(但尚未完成属性填充和初始化方法调用),它会先将自己包装成一个ObjectFactory放入第三级缓存。如果此时Bean A在后续的属性填充过程中需要依赖Bean B,而Bean B又反过来依赖Bean A,那么当Spring尝试创建Bean B并发现它需要Bean A时,它会首先从缓存中查找Bean A。由于Bean A的ObjectFactory已存在于第三级缓存,Spring就能通过这个工厂获取到Bean A的“早期引用”(一个尚未完全初始化的实例)。这个早期引用随后会被放入第二级缓存,供Bean B使用。这样,Bean B就能顺利完成初始化。当Bean B初始化完成后,Bean A可以继续它的初始化过程,填充属性(包括已经完整初始化好的Bean B),并最终完成自身初始化。一旦Bean A完全初始化,它的完整实例会替换掉第二级缓存中的早期引用,并最终被放入第一级缓存。

具体来说,这三级缓存分别是:

  • 一级缓存(singletonObjects): 存放已经完全初始化并可供使用的单例Bean。这是最“成熟”的Bean。
  • 二级缓存(earlySingletonObjects): 存放早期暴露的Bean实例。这些Bean可能已经实例化但尚未完成属性填充或初始化方法调用,也可能已经经过了AOP代理。
  • 三级缓存(singletonFactories): 存放的是ObjectFactory,一个能够生产早期Bean实例的工厂。这个工厂在需要时才会被调用,用于获取Bean的早期引用,特别是在处理AOP代理时显得尤为重要。

Spring三级缓存具体指哪些,它们各自承担了什么职责?

在Spring的单例Bean生命周期管理中,这三层缓存各司其职,共同构筑了一个健壮的循环依赖解决方案。

首先是singletonObjects,这是最核心的一级缓存,它直接映射了Bean名称到其完全初始化、可供使用的单例实例。可以把它想象成一个“成品仓库”,所有经过完整生命周期(实例化、属性填充、初始化方法执行)的Bean最终都会安家于此。当其他Bean需要依赖时,Spring会优先从这里查找。

接着是earlySingletonObjects,这是二级缓存,它扮演了一个“半成品仓库”的角色。当一个Bean被实例化后,但其属性尚未完全填充,或者初始化方法尚未执行时,它的早期引用(可能是原始对象,也可能是经过AOP处理的代理对象)就会被放置在这里。这个缓存的存在,是为了在循环依赖发生时,能够提供一个“临时”的、可用的Bean引用,让依赖它的Bean能够继续初始化。一旦这个Bean完成了所有初始化步骤,它就会从二级缓存中移除,并晋升到一级缓存。

最后,也是最关键的,是singletonFactories,这是三级缓存,它存储的不是Bean实例本身,而是一个ObjectFactory(对象工厂)。这个工厂的职责是“按需生产”Bean的早期引用。为什么需要一个工厂而不是直接存放早期实例呢?这主要是为了解决AOP代理的复杂性。如果一个Bean需要被AOP代理,那么它的早期引用应该是代理对象,而不是原始对象。这个ObjectFactory就封装了生成代理对象的逻辑。只有当其他Bean真正需要这个循环依赖的Bean时,Spring才会调用这个ObjectFactory来获取早期引用。这样,就避免了不必要的AOP代理创建,也确保了获取到的早期引用是正确的(即可能是代理后的)。

这三者协同工作,构成了一个巧妙的“生产线”。当A需要B,B需要A时,A实例化后,其ObjectFactory先入三级缓存。B需要A时,通过三级缓存的ObjectFactory拿到A的早期引用,放入二级缓存供B使用。B初始化完毕,A继续初始化,最终A的完整实例入一级缓存。这种机制确保了在Bean还未完全“成熟”时,也能提供一个可用的引用,从而打破循环依赖的僵局。

为什么需要三级缓存,二级缓存不能解决循环依赖吗?

这是一个非常经典的问题,也是理解Spring循环依赖解决机制深度的关键点。答案是:如果仅仅是纯粹的循环依赖(即没有AOP代理),二级缓存确实可以解决问题。但一旦涉及到AOP(面向切面编程),二级缓存就显得力不从心了,这就是三级缓存存在的根本原因。

想象一下这个场景:Bean A依赖Bean B,Bean B依赖Bean A,并且Bean A还需要被AOP代理。

如果只有一级和二级缓存:

  1. Spring开始创建Bean A,实例化Bean A的原始对象。
  2. 将Bean A的原始对象(早期引用)放入二级缓存earlySingletonObjects
  3. Bean A在填充属性时需要Bean B,Spring开始创建Bean B。
  4. Bean B在填充属性时需要Bean A,从二级缓存earlySingletonObjects中获取到Bean A的原始对象。
  5. Bean B完成初始化。
  6. Bean A继续初始化,此时Spring会执行AOP代理逻辑,生成Bean A的代理对象。
  7. 问题来了:Bean B持有的是Bean A的原始对象引用,而不是AOP代理后的引用。这导致Bean B调用的A的方法不会经过AOP切面,这显然是不正确的行为。

而三级缓存的引入,正是为了解决这个“AOP代理时机”的问题。三级缓存singletonFactories中存储的是一个ObjectFactory,这个工厂能够按需生成Bean的早期引用。这个“按需”和“生成”是关键。

当Bean A实例化后,Spring会将其原始对象包装成一个ObjectFactory放入三级缓存。这个ObjectFactory里面封装了生成AOP代理的逻辑(如果需要代理的话)。

当Bean B需要Bean A时,它会从三级缓存中获取到这个ObjectFactory,然后调用getObject()方法。此时,getObject()方法会判断Bean A是否需要AOP代理。如果需要,它就会立即生成Bean A的代理对象,并将这个代理对象作为早期引用返回给Bean B。这个代理对象随后会被放入二级缓存,同时从三级缓存中移除。

这样一来,Bean B获取到的就是Bean A的“正确”引用——一个可能已经被AOP代理过的对象。当Bean A最终完成所有初始化步骤时,它的一级缓存中的最终实例(也可能是代理对象)会替换掉二级缓存中的早期引用。整个过程中,所有Bean都持有了彼此的正确引用,包括AOP代理后的实例。

所以,三级缓存的真正价值在于它提供了一个“延迟生成AOP代理”的机制,确保了在循环依赖场景下,注入的早期引用是经过AOP处理的(如果需要),从而保证了AOP功能的正确性。没有它,AOP和循环依赖的结合就会出现问题。

Spring是如何判断并触发三级缓存机制来解决循环依赖的?

Spring在Bean的创建过程中,通过一套严谨的内部状态管理和查找逻辑来判断并触发三级缓存机制,从而解决循环依赖。这个过程主要发生在DefaultSingletonBeanRegistry类中的getSingleton()方法内部,这是Spring获取单例Bean的核心入口。

当Spring尝试获取一个单例Bean时(例如,通过getBean()方法),它会按照以下步骤进行:

  1. 尝试从一级缓存(singletonObjects)获取: 这是最直接的查找,如果Bean已经完全初始化并存在于此,直接返回。
  2. 检查Bean是否正在创建中: 如果一级缓存中没有,Spring会检查singletonsCurrentlyInCreation这个Set。这个Set记录了当前正在创建过程中的Bean名称。如果发现当前请求的Bean正在创建中,这就意味着可能存在循环依赖。
  3. 允许早期引用(allowEarlyReference): 对于单例Bean,Spring默认是允许早期引用的。如果Bean正在创建中且允许早期引用,Spring会进一步尝试从二级或三级缓存获取。
  4. 尝试从二级缓存(earlySingletonObjects)获取: 如果Bean正在创建中,并且之前已经有其他Bean请求过它的早期引用,那么这个早期引用可能已经被放入了二级缓存。如果能找到,直接返回这个早期引用。
  5. 尝试从三级缓存(singletonFactories)获取: 如果二级缓存中也没有,但Bean确实正在创建中,Spring会检查三级缓存。如果三级缓存中存在这个Bean对应的ObjectFactory,Spring就会调用这个ObjectFactorygetObject()方法。
    • getObject()方法会负责生成Bean的早期引用。这个早期引用可能是原始的Bean实例,也可能是在AOP增强逻辑作用下生成的代理实例。
    • 一旦通过ObjectFactory获取到早期引用,Spring会立即将这个早期引用放入二级缓存earlySingletonObjects,并同时从三级缓存singletonFactories中移除对应的ObjectFactory。这是为了避免重复生成早期引用,也确保了后续对该Bean早期引用的请求直接从二级缓存获取,效率更高。
  6. 注入并完成初始化: 获取到早期引用后,依赖它的Bean就可以继续完成属性填充和初始化。当这个早期引用所对应的Bean最终完成所有初始化步骤后,它的完整实例会晋升到一级缓存,替换掉之前在二级缓存中的早期引用。

这个机制的关键在于singletonsCurrentlyInCreation这个Set,它就像一个“正在施工”的标志牌。当Spring发现一个Bean正在施工中又被请求时,它就知道可能遇到了循环依赖,于是会启动从二级和三级缓存获取早期引用的逻辑。这种设计确保了只有在真正需要时才触发三级缓存的ObjectFactory,既解决了循环依赖,又兼顾了性能和AOP的正确性。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发 Java
相关文章 更多
精品专题 更多
本月促销

正软商城本月促销专区,汇集办公、设计、安全、影音、系统工具及AI软件等正版软件优惠活动,提供限时折扣、特价授权和优惠购买信息,活动库存及价格以页面实时展示为准。

装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

IOS软件

正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

AI

正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

PDF教程

正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

Mac软件 更多
灵活计算器
灵活计算器

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

图几
图几

图几是一款适用于 macOS 的截图、标注与美化工具,支持离线操作保障隐私。界面整理和高频系统操作被放到一起考虑,桌面或窗口内容一多时,管理起来会更省心。

密码键盘
密码键盘

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

思源笔记
思源笔记

思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

Office 365 简体中文
Office 365 简体中文

一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

WALTR PRO
WALTR PRO

WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

CodeExpander
CodeExpander

CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

Mountain Duck
Mountain Duck

Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

Menuist
Menuist

Menuist 是一款面向 macOS 的 Finder 右键菜单增强工具,主要用来补充新建文件、快捷导航等常用操作,让日常文件管理和访问路径时更高效、更顺手。

Mole
Mole

Mole 是一款专为 Mac 设计的深度清理优化工具,涵盖缓存清理、应用管理及实时状态监控等功能。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

WINDOWS 更多
Windows 10
Windows 10

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

思源笔记
思源笔记

思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

傲梅轻松备份
傲梅轻松备份

傲梅轻松备份是一款专业易用的数据备份软件,为重要数据提供安全保障。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

Office 365 简体中文
Office 365 简体中文

一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

Wise Folder Hider Pro
Wise Folder Hider Pro

Wise Folder Hider Pro 是一款专业级文件和文件夹隐藏加密软件,为私密数据添加多重保护。高频操作更强调就近处理,浏览、整理和跨目录移动文件时,来回切换和重复点击都会少很多。

WALTR PRO
WALTR PRO

WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

CodeExpander
CodeExpander

CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

PinStack
PinStack

PinStack是一款轻量级的Windows平台剪贴板管理工具,优化您的剪贴板使用体验。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

Mountain Duck
Mountain Duck

Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

Seer
Seer

Seer是一款在Win平台下的空格键功能增强效率工具,只需轻敲空格键,就能预览几乎任何格式的文件。它更适合把零散的小功能集中起来使用,处理高频琐碎任务时会更省事。