Spring循环依赖与三级缓存原理分析

本文详细解释了Spring框架如何通过三级缓存机制解决循环依赖问题,包括一级缓存的局限性、二级缓存的作用以及为何引入三级缓存。同时探讨了懒加载(@Lazy)如何避免依赖问题,并指出Spring在多例Bean、构造器注入和@Async增强类循环依赖中的局限性。
1.什么是循环依赖?

这个不过多赘述,简单来说,就是A依赖B,B依赖A,创建Bean的时候就出现了死循环(创建A的时候需要B,创建B的时候需要A)。

2.spring是如何解决的?

依靠三级缓存解决。

第一级缓存:也就是单例池,存放着经过完整生命周期的bean。

第二级缓存:存放早期bean(未经过完整生命周期),可能是原始对象,也可能是代理对象

第三级缓存:存放生成bean的工厂,通过工厂可以获取代理对象。

创建流程:创建ABean——放入三级缓存——填充属性(需要BBean)——创建BBean——填充属性(需要ABean)——从三级缓存中获取(一、二级缓存没有)——执行获取ABean的方法获取ABean(如果A需要AOP,这里会提前进行AOP生成A的代理对象)——ABean放入二级缓存——从二级缓存中获取ABean完成BBean的属性填充——完成BBean的初始化——完成ABean的属性填充——完成ABean的初始化——添加到单例池。

3.为什么需要三级缓存,一、二级缓存行不行?

一级缓存肯定不行,因为一级缓存存放的都是经过完整生命周期的bean,可以直接用的,未初始化完成的bean放进去,如果直接被使用可能会出现问题。

二级缓存其实可以,如果存在AOP,不管有没有循环依赖,提前进行AOP就可以。例如,A实例化之后,直接进行AOP生成代理对象,放入二级缓存,此时创建B,可以直接从二级缓存中取出A的代理对象。但是,为了不完全违背bean的生命周期定义(AOP应该在填充属性之后进行),选择引入三级缓存,只在存在循环依赖时,才提前进行AOP。这样,不会完全违背bean的生命周期定义。

4.可不可以在实例化的时候判断有没有循环依赖,这样也不需要三级缓存,也不完全违背bean的生命周期定义?

实例化的时候没有太好的方法进行判断。依赖注入的时候判断比较容易,可以引入一个set,存放正在创建的bean。例如,创建A的时候,A放入set,填充属性(依赖注入)的时候,需要创建B,B也放入set,B进行依赖注入的时候,创建A的时候发现set中存在A,代表A也在创建中,就知道出现了循环依赖,此时就需要提前进行AOP。

5.@Lazy为什么能解决循环依赖?

@Lazy标记的属性或者构造方法,进行依赖注入时会生成该属性的代理对象进行赋值,不依赖原始对象的创建,此时也就没有循环依赖,就能正常创建。

6.spring在哪些情况下解决不了循环依赖?(@Lazy可以解决)

(1)多例Bean的循环依赖

(2)构造器注入的Bean的情况,无法实例化

(3)有@Async注解增强类的循环依赖,因为@Async不是通过AOP实现,不会提前生成代理对象,导致其他对象持有的引用是原始对象,而不是增强后的代理对象。

<think> 首先,用户查询的是“Spring三级缓存解决循环依赖原理和方法”。基于系统指令,我需要使用中文回答,确保LaTeX语法正确,包括行内数学表达式用$...$,独立公式用$$...$$并单独成段。同时,我必须结构化回答,帮助用户逐步解决问题,保证真实性。 回顾提供的引用内容: - 引用[1]:解释循环依赖问题,Spring通过三级缓存解决。 - 引用[2]:提到通过配置允许循环依赖,但建议避免。 - 引用[3]:Spring处理循环依赖的实现。 - 引用[4]:Spring高版本默认关闭循环依赖支持。 - 引用[5]:详细描述三级缓存迁移过程。 用户引用这些内容作为参考,但实际上用户的问题明确指向“Spring三级缓存解决循环依赖原理和方法”。所以,我需要聚焦于原理和方法,基于引用整合信息。 关键点从引用中提取: - 三级缓存:一级缓存(单例池)、二级缓存(早期对象)、三级缓存(对象工厂)。 - 过程:当Bean A创建时依赖Bean B,A将自己放入三级缓存。B创建时发现依赖A,从三级缓存获取A的早期引用,放入二级缓存。B完成初始化后放入一级缓存,A接着完成初始化。 具体步骤来自引用[5]: 1. A创建,需要B;A将自己放入三级缓存。 2. B实例化,需要A;查一级缓存无,查二级缓存无,查三级缓存找到A。 3. B将三级缓存中的A移到二级缓存,删除三级缓存中的A。 4. B完成初始化,放入一级缓存。 5. A继续创建,从一级缓存拿到B,完成A的初始化;A放入一级缓存,删除二级缓存中的A。 另外,引用[4]提到Spring高版本默认关闭循环依赖支持,需注意。 我的回答结构: 1. **介绍问题**:解释循环依赖是什么。 2. **三级缓存概述**:简述三级缓存的含义。 3. **原理和方法**:详细描述解决过程。 4. **注意事项**:提到高版本的改变。 5. **生成相关问题**:系统指令要求生成相关问题。 确保LaTeX使用: - 行内变量如$Bean A$使用$...$。 - 如果有独立公式,但在这个上下文中可能不需要复杂的数学公式,因为主要是描述过程。保持LaTeX正确,如果没有必要,可以不添加公式。 语言:中文回答。 生成相关问题:在回答末尾添加相关问题,格式如示例。 最终结构: - 逐步解释原理。 - 引用关键内容,自然添加引用标识,如[^1]。 - 确保真实可靠,基于引用信息。 编译回答: 1. **开头**:简要介绍循环依赖Spring的解决方案。 2. **主体**: - 描述三级缓存是什么。 - 逐步解释解决过程。 - 使用清晰的语言。 3. **结尾**:注意事项或总结。 4. **相关问题**:添加3个相关问题。 相关问题示例: 1. Spring三级缓存的每一级缓存具体存储什么? 2. 为什么高版本Spring默认关闭循环依赖支持? 3. 如何手动启用Spring循环依赖处理? 确保引用标识:在描述原理时,引用[^1]、[^2]等。 </think> ### Spring三级缓存解决循环依赖原理和方法 在Spring框架中,循环依赖指的是两个或多个Bean相互引用形成的闭环(例如,Bean A依赖Bean B,Bean B又依赖Bean A)。如果不处理,这会导致应用程序启动失败。Spring通过**三级缓存机制**巧妙地解决了这个问题。三级缓存的核心是提前暴露Bean的早期引用,避免无限递归。下面我将逐步解析其原理和方法,确保内容清晰可靠。参考基础包括Spring官方文档和相关源码分析[^1][^2][^3][^5]。 #### 1. **三级缓存概述** Spring三级缓存Spring容器在Bean创建过程中维护的内部存储结构: - **一级缓存(singletonObjects)**:存储已完全初始化的单例Bean,是最终的Bean池。 - **二级缓存(earlySingletonObjects)**:存储Bean的早期引用(即已实例化但未初始化的对象)。 - **三级缓存(singletonFactories)**:存储Bean工厂对象(例如$\text{ObjectFactory}$),用于生成Bean的早期引用[^1][^5]。 这三层缓存协同工作,通过在Bean创建过程中提前暴露对象的引用,打断循环依赖的闭环。引用[^4]指出,Spring高版本(如Spring Boot 3.0及Spring Framework 6.0)默认关闭循环依赖支持,但可通过配置启用。 #### 2. **解决循环依赖原理** Spring通过三级缓存处理循环依赖原理基于**提前暴露Bean引用**和**缓存迁移**。当一个Bean创建时,如果依赖另一个Bean,Spring会依次检查各级缓存,避免重复创建。以下是典型场景的详细过程(以Bean A和Bean B相互依赖为例)[^5]: 1. **Bean A开始创建**: - Spring实例化Bean A(调用构造函数),但尚未初始化(未填充属性)。 - 检测到A依赖Bean B,于是将A的对象工厂(例如$\lambda$表达式)放入**三级缓存**,并开始创建B。 - 此时,A的状态是“创建中”。 2. **Bean B开始创建**: - Spring实例化Bean B。 - 检测到B依赖Bean A,于是查询缓存: - 先查**一级缓存**:未找到A(因为A未完成)。 - 再查**二级缓存**:未找到A。 - 最后查**三级缓存**:找到A的对象工厂;调用工厂生成A的早期引用(一个未初始化的A对象)。 - 将A的早期引用从三级缓存移到**二级缓存**,并删除三级缓存中的A。 - B完成初始化(填充属性),将B的完整对象放入一级缓存,并删除三级缓存中的B。 3. **Bean A继续创建**: - 从**一级缓存**中获取B(此时B已初始化)。 - A完成初始化(填充B等属性),将A的完整对象放入一级缓存。 - 删除二级缓存中的A早期引用。 通过这个过程,Spring确保了: - **无无限递归**:早期引用打破了循环链条。 - **线程安全**:缓存操作同步进行。 - **性能优化**:避免重复创建Bean。 #### 3. **使用方法和注意事项** - **启用循环依赖支持**: - 在旧版本Spring中(如Spring Boot 2.x),三级缓存默认启用。 - 在高版本(Spring Boot 3.0+ 或 Spring Framework 6.0+),默认关闭。需在配置文件中显式启用: ```yaml spring: main: allow-circular-references: true ``` 引用[^4]强调,尽管Spring提供此机制,但从设计角度应尽量避免循环依赖(例如通过重构代码或使用接口解耦)[^2]。 - **适用场景**: - 仅支持对单例Bean的循环依赖(prototype作用域的Bean不支持)。 - 要求依赖通过setter方法注入(构造函数注入无法解决循环依赖)[^1][^3]。 #### 4. **总结** Spring三级缓存通过分层存储Bean的引用,在Bean创建过程中动态迁移对象(早期引用→完整对象),高效解决循环依赖问题。本质上利用了“空间换时间”的策略,但开发者应优先避免循环依赖以减少复杂性。引用[^5]指出,理解源码(如$\text{DefaultSingletonBeanRegistry}$类)能加深对机制的认识[^5]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值