Spring三级缓存解决循环依赖的解析过程
一、循环依赖场景
假设存在两个bean的相互依赖:
@component public class servicea { @autowired private serviceb serviceb; } @component public class serviceb { @autowired private servicea servicea; }
二、三级缓存定义
在 defaultsingletonbeanregistry
中定义:
// 一级缓存:完整bean(k:bean名称 v:实例化+初始化完成的bean) private final mapsingletonobjects = new concurrenthashmap<>(256); // 二级缓存:早期暴露对象(k:bean名称 v:未完成属性注入的原始bean) private final map earlysingletonobjects = new hashmap<>(16); // 三级缓存:对象工厂(k:bean名称 v:objectfactory) private final map > singletonfactories = new hashmap<>(16);
三、解决流程(以servicea和serviceb为例)
1. 创建servicea
sequencediagram participant container participant cache1 as singletonobjects participant cache2 as earlysingletonobjects participant cache3 as singletonfactories container->>cache1: 检查servicea是否存在 cache1-->>container: 不存在 container->>container: 实例化servicea(构造器调用) container->>cache3: 添加servicea的objectfactory container->>container: 开始属性注入(需要serviceb)
关键代码段:
// abstractautowirecapablebeanfactory#docreatebean addsingletonfactory(beanname, () -> getearlybeanreference(beanname, mbd, bean));
2. 发现需要serviceb
sequencediagram participant container participant cache1 participant cache2 participant cache3 container->>cache1: 检查serviceb是否存在 cache1-->>container: 不存在 container->>container: 实例化serviceb(构造器调用) container->>cache3: 添加serviceb的objectfactory container->>container: 开始属性注入(需要servicea)
3. 解决serviceb对servicea的依赖
sequencediagram participant container participant cache1 participant cache2 participant cache3 container->>cache1: 查找servicea cache1-->>container: 不存在 container->>cache2: 查找servicea cache2-->>container: 不存在 container->>cache3: 获取servicea的objectfactory container->>container: 执行getearlybeanreference() container->>cache2: 将生成的代理对象存入earlysingletonobjects container->>serviceb: 注入servicea的早期引用 container->>container: 完成serviceb初始化 container->>cache1: 将serviceb放入singletonobjects
4. 回溯完成servicea初始化
sequencediagram participant container participant cache1 participant cache2 participant cache3 container->>servicea: 注入已初始化的serviceb container->>container: 执行servicea的初始化后方法 container->>cache1: 将servicea放入singletonobjects container->>cache2: 移除servicea的早期引用 container->>cache3: 移除servicea的objectfactory
四、关键机制详解
1. getearlybeanreference() 的核心作用
protected object getearlybeanreference(string beanname, rootbeandefinition mbd, object bean) { // 如果有必要,在此处生成代理对象 if (!mbd.issynthetic() && hasinstantiationawarebeanpostprocessors()) { for (beanpostprocessor bp : getbeanpostprocessors()) { if (bp instanceof smartinstantiationawarebeanpostprocessor) { smartinstantiationawarebeanpostprocessor ibp = (smartinstantiationawarebeanpostprocessor) bp; bean = ibp.getearlybeanreference(exposedobject, beanname); } } } return bean; }
2. 三级缓存必要性分析
缓存级别 | 解决的问题 | 典型场景 |
---|---|---|
singletonfactories | 处理aop代理的延迟生成 | 需要保证代理对象的单例性 |
earlysingletonobjects | 避免重复创建早期引用 | 多个bean依赖同一个未完成初始化的bean |
singletonobjects | 存储最终可用bean | 正常bean获取 |
五、设计约束与限制
1.仅支持单例作用域
原型(prototype)作用域的bean无法解决循环依赖,因为spring不缓存原型bean
2.构造器注入限制
如果循环依赖通过构造器注入发生,无法解决(实例化前就需要完成依赖注入)
3.异步初始化风险
使用@async
等方法增强的bean可能破坏初始化顺序
六、调试技巧
查看缓存状态
在 defaultsingletonbeanregistry
类中设置断点:
// 查看三级缓存内容 system.out.println("singletonfactories: " + singletonfactories.keyset()); system.out.println("earlysingletonobjects: " + earlysingletonobjects.keyset()); system.out.println("singletonobjects: " + singletonobjects.keyset());
强制抛出循环依赖异常
在配置类添加:
@bean public circularreferencesbean circularreferencesbean() { return new circularreferencesbean(circularreferencesbean()); }
七、性能优化建议
避免过度使用循环依赖
即使技术可行,也应通过设计模式(如事件驱动)解耦
合理使用@lazy注解
延迟加载非必要依赖:
@autowired @lazy private serviceb serviceb;
监控缓存命中率
通过jmx监控 singletonobjects
与 earlysingletonobjects
的比例
总结
spring的三级缓存机制通过 提前暴露对象引用 + 动态代理生成 的协同设计,在保证单例性的前提下,优雅地解决了循环依赖问题。理解该机制需要重点把握bean生命周期的阶段划分和缓存状态的转换逻辑。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
推荐阅读
-
IDEA中使用Gradle构建项目中文报GBK错误的解决方案
-
将Java应用做成exe可执行软件的流程步骤
-
SpringBoot实现多种来源的Zip多层目录打包下载
需要将一批文件(可能分布在不同目录、不同来源)打包成zip格式,按目录结构导出给用户下载。1.核心思路支持将本地服务器上的文...
-
Java中减少if-else的设计模式和优化技巧
前言“过于依赖if-else不仅会让代码变得臃肿不堪,还会使维护成本大大增加。其实,if-else虽然是最基础的条件分支,...
-
Spring Boot 中使用 Drools 规则引擎的完整步骤
-
Spring Boot整合Drools规则引擎实战指南及最佳实践
一、drools简介与核心概念1.1什么是drools?drools是redhat旗下的开源业务规则管理系统(brms),...
-
Springboot项目瘦身之如何将jar包与lib依赖分开打包
将jar包与lib依赖分开打包方法一:项目和依赖完全分离maven-jar-plugin负责生成jar文件(jar文件中...
-
Spring动态修改bean属性配置key的几种方法
静态配置的局限性先来看一个典型场景。假设我们有一个数据源配置类:@configuration@configurationpr...
-
Java如何判断一个IP是否在给定的网段内
-
从零开始学java之二叉树和哈希表实现代码