Spring 三级缓存 vs 二级缓存:深度解析循环依赖的终极解决方案

发布于:2025-03-05 ⋅ 阅读:(32) ⋅ 点赞:(0)

目录

一. 循环依赖的本质矛盾

1. 什么是循环依赖?

2. 矛盾的核心

二.  三级缓存架构解析

三级缓存工作流程图​编辑

三、为什么必须三级缓存?

1. 二级缓存的致命缺陷

2. 三级缓存的精妙设计

四、场景推演:三级缓存如何解决代理问题

1. 正常流程(无循环依赖)

2. 循环依赖流程 ​编辑

五、源码级验证

1. 三级缓存获取逻辑

 2. 代理对象生成点

六、如果强行使用二级缓存

在 Spring 框架中,循环依赖(Circular Dependency)是开发过程中最常见的陷阱之一。Spring 通过三级缓存机制优雅地解决了这一问题,但许多开发者会疑惑:为什么需要三级缓存?二级缓存能否实现同样的效果?本文将通过源码分析、场景推演和设计哲学,揭示这一机制背后的精妙设计。


一. 循环依赖的本质矛盾

1. 什么是循环依赖?

当两个或多个 Bean 相互依赖时,就会形成循环依赖:

// Bean A 依赖 Bean B
@Component
public class A {
    @Autowired
    private B b;
}

// Bean B 依赖 Bean A
@Component
public class B {
    @Autowired
    private A a;
}

2. 矛盾的核心

  • 构造器注入无法解决循环依赖:必须通过 Setter 或字段注入。

  • Bean 创建的生命周期冲突

二.  三级缓存架构解析

Spring 通过三级缓存解决循环依赖问题,其核心数据结构如下:

缓存级别 数据结构 存储内容
一级缓存 singletonObjects 完全初始化完成的单例 Bean
二级缓存 earlySingletonObjects 提前暴露的早期 Bean(未完成属性注入)
三级缓存 singletonFactories Bean 的 ObjectFactory(用于生成代理对象)

三级缓存工作流程图

三、为什么必须三级缓存?

1. 二级缓存的致命缺陷

假设只有一级缓存 + 二级缓存

  • 场景:Bean A 依赖 Bean B,Bean B 依赖 Bean A,且 A 需要 AOP 代理

  • 问题链

    1. A 实例化后放入二级缓存(原始对象)

    2. B 从二级缓存获取 A 的原始对象并注入

    3. A 完成初始化后需要生成代理对象

    4. 结果:B 中持有的是 A 的原始对象,与最终代理对象不一致

2. 三级缓存的精妙设计

通过 ObjectFactory 延迟处理代理:

// AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, Object bean) {
    // 通过后置处理器生成代理对象
    return applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
  • 动态决策:只有在发生循环依赖时才会调用 ObjectFactory

  • 代理一致性:保证所有依赖方拿到的是同一个代理对象


四、场景推演:三级缓存如何解决代理问题

1. 正常流程(无循环依赖)

2. 循环依赖流程 

五、源码级验证

1. 三级缓存获取逻辑

// DefaultSingletonBeanRegistry
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

 2. 代理对象生成点

// AbstractAutowireCapableBeanFactory
protected Object doCreateBean(...) {
    // 1. 实例化对象
    instanceWrapper = createBeanInstance(beanName, mbd, args);
    
    // 2. 加入三级缓存
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    
    // 3. 属性填充(可能触发循环依赖)
    populateBean(beanName, mbd, instanceWrapper);
    
    // 4. 初始化(生成最终代理对象)
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}

六、如果强行使用二级缓存?

假设合并三级缓存中的 singletonFactories 和 earlySingletonObjects,会导致:

  1. 提前暴露未完成对象:可能将半成品 Bean 暴露给其他线程

  2. 代理对象不一致:普通 Bean 与代理 Bean 可能同时存在

  3. 内存泄漏风险:无法及时清理创建失败的 Bean 引用


网站公告

今日签到

点亮在社区的每一天
去签到