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

 更新时间:2025年05月04日 21:02:44   作者:时光不负追梦人  
这篇文章主要介绍了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 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 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监控 singletonObjectsearlySingletonObjects 的比例

总结

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中使用opencsv读写csv文件示例

    Java中使用opencsv读写csv文件示例

    这篇文章主要介绍了Java中使用opencsv读写csv文件示例,本文给出了读CSV文件、写CSV文件、自定义分隔符、生成Javabeans等内容,需要的朋友可以参考下
    2015-04-04
  • Spring4.0 MVC请求json数据报406错误的解决方法

    Spring4.0 MVC请求json数据报406错误的解决方法

    这篇文章主要为大家详细介绍了Spring4.0 MVC请求json数据报406错误的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Windows下apache ant安装、环境变量配置教程

    Windows下apache ant安装、环境变量配置教程

    这篇文章主要介绍了Windows下apache ant安装、环境变量配置教程,ANT的安装很简单,本文同时讲解了验证安装是否成功的方法和使用方法实例,需要的朋友可以参考下
    2015-06-06
  • java实现滑动验证解锁

    java实现滑动验证解锁

    这篇文章主要为大家详细介绍了java实现滑动验证解锁,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • 重写equals的同时为何要重写hashCode?

    重写equals的同时为何要重写hashCode?

    这篇文章主要给大家介绍了关于重写equals的同时为何要重写hashCode的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Java 数组内置函数toArray详解

    Java 数组内置函数toArray详解

    这篇文章主要介绍了Java 数组内置函数toArray详解,文本详细的讲解了toArray底层的代码和文档,需要的朋友可以参考下
    2021-06-06
  • SpringBoot的ResponseEntity类返回给前端具体讲解

    SpringBoot的ResponseEntity类返回给前端具体讲解

    这篇文章主要给大家介绍了关于SpringBoot的ResponseEntity类返回给前端的相关资料,ResponseEntity是Spring框架中用于封装HTTP响应的类,可以自定义状态码、响应头和响应体,常用于控制器方法中返回特定数据的HTTP响应,需要的朋友可以参考下
    2024-11-11
  • Spring Boot + EasyExcel + SqlServer 进行批量处理数据的高效方法

    Spring Boot + EasyExcel + SqlServer 进行批量处理数据的高效方法

    在日常开发和工作中,我们可能要根据用户上传的文件做一系列的处理,本篇文章就以Excel表格文件为例,主要介绍了Spring Boot + EasyExcel + SqlServer 进行批量处理数据的高效方法,需要的朋友可以参考下
    2024-06-06
  • Java 实现文件批量重命名亲测可用(精简版)

    Java 实现文件批量重命名亲测可用(精简版)

    本文给大家分享一段自己写的java代码实现文件批量重命名,亲测试过没有任何问题,大家可以放心使用
    2016-11-11
  • Spring Security OAuth2实现使用JWT的示例代码

    Spring Security OAuth2实现使用JWT的示例代码

    这篇文章主要介绍了Spring Security OAuth2实现使用JWT的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09

最新评论