java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring三级缓存解决循环依赖

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

作者:时光不负追梦人

这篇文章主要介绍了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生命周期的阶段划分和缓存状态的转换逻辑。

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

您可能感兴趣的文章:
阅读全文