Spring三级缓存解决循环依赖的解析过程

2025-05-05 18:50

本文主要是介绍Spring三级缓存解决循环依赖的解析过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Spring三级缓存解决循环依赖的解析过程》:本文主要介绍Spring三级缓存解决循环依赖的解析过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教...

一、循环依赖场景

假设存在两个Bean的相互依赖:

@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Component
public class SerRwpReGcQviceB {
    @Autowired
    private ServiceA serviceA;
}

二、三级缓存定义

DefaultSingletonBeanRegistry 中定义:

// 一级缓存:完整Bean(K:Bean名称 V:实例化+初始化完成的Bean)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存:早期暴露对象(K:Bean名称 V:未完成属性注入的原始Bean)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

// 三级缓存:对象工厂(K:Bean名称 V:ObjectFactory)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

三、解决流程(以ServiceA和ServiceB为例)

1. 创建ServiceA

sequenceDiagram
    participant Conthttp://www.chinasem.cnainer
    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 SmartInstantiationAwjavascriptareBeanPostProcessor) {
                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监控 singletonjavascriptObjectsearlySingletonObjects 的比例

总结

Spring的三级缓存机制通过 提前暴露对象引用 + 动态代理生成 的协同设计,在保证单例性的前提下,优雅地解决了循环依赖问题。理解该机制需要重点把握Bean生命周期的阶段划分和缓存状态的转换逻辑。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程China编程(www.chinasem.cn)。

这篇关于Spring三级缓存解决循环依赖的解析过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154492

相关文章

Django HTTPResponse响应体中返回openpyxl生成的文件过程

《DjangoHTTPResponse响应体中返回openpyxl生成的文件过程》Django返回文件流时需通过Content-Disposition头指定编码后的文件名,使用openpyxl的sa... 目录Django返回文件流时使用指定文件名Django HTTPResponse响应体中返回openp

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

批量导入txt数据到的redis过程

《批量导入txt数据到的redis过程》用户通过将Redis命令逐行写入txt文件,利用管道模式运行客户端,成功执行批量删除以Product*匹配的Key操作,提高了数据清理效率... 目录批量导入txt数据到Redisjs把redis命令按一条 一行写到txt中管道命令运行redis客户端成功了批量删除k

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Win10安装Maven与环境变量配置过程

《Win10安装Maven与环境变量配置过程》本文介绍Maven的安装与配置方法,涵盖下载、环境变量设置、本地仓库及镜像配置,指导如何在IDEA中正确配置Maven,适用于Java及其他语言项目的构建... 目录Maven 是什么?一、下载二、安装三、配置环境四、验证测试五、配置本地仓库六、配置国内镜像地址

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security