跳转至

Spring 循环依赖与三级缓存

一、什么是循环依赖

A 依赖 B,B 依赖 A:

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

二、三级缓存

singletonObjects       // 一级:完整 Bean
earlySingletonObjects  // 二级:早期 Bean(半成品)
singletonFactories     // 三级:ObjectFactory(用于生成早期引用)

三、解决流程

  1. 创建 A:实例化 A(属性还没注入)。
  2. 把 A 的 ObjectFactory 放入三级缓存
  3. 注入 A 的属性:发现需要 B,去创建 B。
  4. 创建 B:实例化 B,注入属性,发现需要 A。
  5. 从三级缓存拿 A 的 ObjectFactory,调用 getObject() 得到 A 的早期引用,放入二级缓存,从三级删除。
  6. B 拿到 A,属性注入完成,B 放入一级缓存。
  7. A 拿到 B,属性注入完成,A 放入一级缓存。

四、为什么需要三级

  • 一级:成品。
  • 二级:半成品(已实例化未填充)。
  • 三级:ObjectFactory,用于处理 AOP(如果 A 被代理,提前生成代理对象)。

两级缓存也行,但没法处理 AOP 循环依赖。三级缓存的 ObjectFactory 决定"要不要提前生成代理"。

五、什么循环依赖解决不了

  • 构造器循环依赖:实例化阶段就需要对方,没法提前暴露。
  • prototype 作用域:每次新建,不缓存。

六、Spring Boot 2.6+ 默认禁止循环依赖

要在 application.yml 开:

spring:
  main:
    allow-circular-references: true

最佳实践

循环依赖是设计问题,尽量重构。用 setter 注入或 @Lazy 缓解。