java-spring三级缓存解决循环依赖

本文探讨了Spring框架中如何利用三级缓存机制解决循环依赖问题。通过详细解析A类与B类之间的依赖关系,展示了不同注入方式下(如set方法注入、属性注入和构造方法注入)循环依赖的具体表现及解决方案。

环境准备

A类中依赖B类

B类中依赖A类
Spring Bean的初始化大致分为两个阶段,第一个阶段,通过反射创建Bean,此时bean是前期对象,并没有赋值,也就是A类通过反射创建之后,他依赖的B类为null;第二个阶段,给早期对象赋值,也就是将A类中的B类初始化。

三级缓存解决循环依赖,三个缓存整个bean初始化的分布情况

1、set方法注入时,循环依赖实在实例化A对象之后,加入到三级缓存池中,给A对象赋值的 时候,发现有依赖B对象。此时去创建B对象,B对象实例化完成之后,加入到三级缓存池中。 此时缓存池的情况(1级:没有,2级没有,3级A\B对象的早期对象)。B对象在赋值的时候, 发现依赖A对象,于是创建A对象,发现三级缓存池中存在A对象,那么此时直接从三级缓存 中获取到A对象,并从三级缓存中删除A对象,且把A对象添加到二级缓存池中。缓存池情况 (1、没有,2、A对象,3、B对象),于是B对象的赋值完成,走后面的逻辑:将B对象添加到 一级缓存之中,并把2、3级缓存池中的B对象删除。缓存池状态(1、B完整对象,2、A的早期 对象,3、没有)。此时B对象完成走A对象的赋值操作,A对象赋值成功,走A对象的后期逻辑 ,添加到1级缓存池中,并把2、3级缓存池中的A对象删除,缓存池状态(1、A、B完整对象, 2、没有,3、没有)。整个A对象实例化完成。走实例化B对象时,发现一级缓存池中已经 存在,直接获取到返回。

2、属性注入的方式,与set方法注入方式相同,也能解决循环依赖问题。

3、构造方式注入时,在实例化A对象的时候,就发现依赖B对象,此时就去创建B对象了, 而此时的A对象并没有加入三级缓存池中,B对象实例化时,发现依赖A对象,又去创建 A对象了,发现A对象正在创建,抛出异常,循环依赖问题。

所以,一般情况下,属性注入以及set方法注入时,可解决循环依赖问题,构造函数注入时,无法解决循环依赖问题,当有循环依赖问题时,会抛出异常。

对于多例对象的循环依赖,spring没有添加缓存中,每次使用都要new一下,所以多例无法解决循环依赖

<think>嗯,用户想了解Spring框架如何通过三级缓存解决Bean之间的循环依赖问题,并且希望有图解说明。我需要先回忆一下Spring三级缓存机制。Spring在创建Bean的时候,如果遇到循环依赖,比如A依赖B,B又依赖A,这时候需要机制来避免无限循环。三级缓存分别是singletonFactories、earlySingletonObjects和singletonObjects。 首先,应该解释三个缓存的作用。singletonFactories存放的是生成早期Bean的工厂,earlySingletonObjects存放的是还未完全初始化的Bean实例,singletonObjects存放的是完全初始化好的Bean。当创建A的时候,A的工厂会被放入singletonFactories,接着填充属性时发现需要B,于是开始创建B。B在填充属性时又需要A,这时候会从singletonFactories中找到A的工厂,生成早期对象放入earlySingletonObjects,并移除对应的工厂。这样B就能完成初始化,放入singletonObjects,然后A也能继续完成初始化。 用户可能对这三个缓存的具体使用流程不太清楚,需要一步步说明。同时,图解的话,可能需要用文字描述流程图,比如步骤1到步骤N,每个步骤涉及哪个缓存的变化。另外,需要提到Spring只能解决单例模式下通过setter注入的循环依赖,构造器注入无法解决,这点很重要,用户可能在实际开发中遇到类似问题需要注意。 还要注意引用部分,用户提到了引用[1]和引用[2],但引用内容似乎和Spring无关,可能是示例中的占位符。不过根据系统指令,可能需要引用相关文献,但用户提供的引用可能与主题无关,需要确认是否有其他来源。不过按照用户给的参考,可能不需要额外引用,或者需要指出Spring官方文档作为参考。 最后,生成相关问题的时候,应该围绕循环依赖的其他解决方式、三级缓存的局限性,以及与构造器注入的关系来提问,这样能帮助用户进一步理解。</think>### Spring框架三级缓存机制解决循环依赖的图解说明 Spring通过三级缓存机制解决单例Bean的循环依赖问题[^1],核心流程分为以下步骤: $$流程图$$ 创建A → 存入三级缓存 → 填充属性B → 创建B → 填充属性A → 从三级缓存获取A的半成品 → 完成B的初始化 → 完成A的初始化 $$ #### 三级缓存结构说明 | 缓存层级 | 数据结构 | 存储内容 | |---------|---------|---------| | 一级缓存 | singletonObjects | 完成初始化的完整Bean | | 二级缓存 | earlySingletonObjects | 已实例化但未初始化的早期引用 | | 三级缓存 | singletonFactories | ObjectFactory工厂对象 | #### 执行流程分解(以A→B→A循环依赖为例): 1. **创建Bean A** - 通过`getBean("A")`触发实例化 - 调用构造器创建A的原始对象(此时对象属性为空) - 将A的ObjectFactory存入三级缓存`singletonFactories` 2. **填充属性阶段** ```java // 伪代码示例 populateBean(){ if (需要注入B) { getBean("B") // 触发B的创建流程 } } ``` 3. **创建Bean B** - 通过`getBean("B")`触发实例化 - 调用构造器创建B的原始对象 - 将B的ObjectFactory存入三级缓存 4. **B填充属性A** - 执行`getBean("A")`时: - 在一级缓存未找到 -三级缓存找到A的ObjectFactory - 通过`getObject()`获取A的早期引用 - 将A早期引用移入二级缓存`earlySingletonObjects` -三级缓存删除A的工厂对象 5. **完成B的初始化** - B持有A的早期引用 - 将完整B对象存入一级缓存`singletonObjects` - 清除B在二、三级缓存的记录 6. **完成A的初始化** - A获得完整B对象的引用 - 执行初始化后处理器 - 将完整A对象存入一级缓存 #### 关键约束条件 1. 仅支持**setter/field注入**方式的循环依赖 2. 构造器注入无法通过此机制解决 3. 要求Bean必须是**单例作用域** 4. 不支持原型(prototype)作用域的Bean ```plantuml @startuml participant "一级缓存" as L1 participant "二级缓存" as L2 participant "三级缓存" as L3 participant "Bean A" as A participant "Bean B" as B A -> L3: 存入A的ObjectFactory A -> B: getBean("B") B -> L3: 存入B的ObjectFactory B -> L3: 查询A的ObjectFactory L3 --> B: 返回A的早期对象 B -> L2: 存入A的早期引用 B -> L1: 存入完整B对象 A -> L1: 获取完整B对象 A -> L1: 存入完整A对象 @enduml ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值