Cool
Cool
Published on 2025-05-05 / 14 Visits
0
0

Spring三级缓存解决循环依赖的深度解析

Spring三级缓存解决循环依赖的深度解析

一、问题背景:什么是循环依赖?

在Spring应用中,当两个或多个Bean相互依赖时,会形成循环依赖。例如:

@Service
public class A {
    @Autowired
    private B b;
}

@Service
public class B {
    @Autowired
    private A a;
}

这种场景下,Spring需要解决两个核心问题:

  1. 如何避免Bean创建时的死锁?
  2. 如何保证AOP代理对象的正确性?

二、Spring的三级缓存结构

Spring通过三个层级的缓存管理Bean的生命周期:

缓存层级存储内容作用
一级缓存(singletonObjects)完全初始化的Bean直接对外提供可用的Bean
二级缓存(earlySingletonObjects)提前暴露的早期Bean(未初始化)解决循环依赖时的临时存储
三级缓存(singletonFactories)ObjectFactory工厂对象延迟生成代理对象,保证一致性

三、三级缓存解决循环依赖的流程

以A、B循环依赖为例,详细流程如下:

1. 创建Bean A

  • 实例化A:通过构造函数创建原始对象(未注入属性、未初始化)。
  • 暴露早期引用:将A的工厂对象存入三级缓存:
    singletonFactories.put("a", () -> getEarlyBeanReference(bean));
    
  • 属性填充:尝试注入B,触发Bean B的创建。

2. 创建Bean B

  • 实例化B:同理,创建B的原始对象,暴露工厂到三级缓存。
  • 属性填充:尝试注入A:
    • 从三级缓存获取A的ObjectFactory,生成早期引用(若需要代理,此时生成)。
    • 将A的早期引用升级到二级缓存。
  • 完成B的初始化:执行@PostConstruct,移入一级缓存。

3. 完成Bean A的创建

  • 注入B:从一级缓存获取已初始化的B。
  • 完成A的初始化:执行@PostConstruct,移入一级缓存,清理二级/三级缓存。

四、为什么必须使用三级缓存?

1. 处理AOP代理的矛盾

  • 问题:若直接暴露原始实例,后续生成的代理对象会导致注入不一致。
  • 解决方案:通过三级缓存的ObjectFactory,确保首次访问时生成代理,后续所有引用指向同一代理对象。

2. 避免过早代理

  • 问题:若在二级缓存直接生成代理,可能错过@PostConstruct等初始化逻辑。
  • 解决方案:三级缓存延迟代理生成,仅在真正需要时触发。

3. 保证线程安全

  • 通过分层设计,确保在多线程环境下,早期引用的生成和暴露是原子操作

五、代码示例(Spring源码片段)

protected Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null) {
                ObjectFactory<?> factory = this.singletonFactories.get(beanName);
                if (factory != null) {
                    // 通过工厂生成早期引用(代理在此生成)
                    singletonObject = factory.getObject(); 
                    // 升级到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

六、总结

Spring三级缓存的设计巧妙地平衡了以下需求:

  • 循环依赖的解决:通过提前暴露未初始化对象的引用。
  • AOP代理的一致性:通过工厂模式延迟生成代理对象。
  • 生命周期管理:分层缓存隔离不同阶段的Bean状态。

这种机制既保证了Bean的完整初始化,又兼容了Spring AOP等高级特性,是Spring框架高度灵活和健壮的关键设计之一。


Comment