Spring 框架中的循环依赖问题

Spring 框架中的循环依赖是指多个 Bean 之间形成依赖闭环的情况,例如 Bean A 依赖 Bean B,而 Bean B 又依赖 Bean A。Spring 通过三级缓存机制解决单例模式下属性注入的循环依赖问题,但无法处理构造器注入和非单例场景(如原型作用域)。以下是详细解析:


一、循环依赖的类型与限制

  1. 构造器循环依赖

    // 构造器相互依赖示例
    @Component
    public class A {
        private B b;
        public A(B b) { this.b = b; } // 构造器依赖 B
    }
    @Component
    public class B {
        private A a;
        public B(A a) { this.a = a; } // 构造器依赖 A
    }
    
    • 结果:Spring 容器启动时会直接抛出 BeanCurrentlyInCreationException 异常,因为构造器需要在对象实例化前完成,无法通过提前暴露对象引用解决。
  2. 属性/Setter 方法循环依赖
    使用 @Autowired 或 XML 配置的 Setter 方法注入时,Spring 可通过三级缓存解决单例 Bean 的循环依赖。

  3. 非单例 Bean 的循环依赖
    原型(Prototype)作用域的 Bean 无法解决循环依赖,因为每次请求都会创建新实例,无法通过缓存机制处理。


二、三级缓存机制的原理

Spring 通过以下三级缓存解决单例属性循环依赖:

缓存层级名称描述
一级缓存singletonObjects存储完全初始化后的单例 Bean
二级缓存earlySingletonObjects存储早期曝光的 Bean(未完成属性注入)
三级缓存singletonFactories存储 Bean 工厂,用于生成早期对象代理(含 AOP 处理逻辑)
解决流程(以 A←→B 循环依赖为例):
1. 实例化 A → 生成原始对象 A,并将 A 的 ObjectFactory 存入三级缓存
2. 注入 A 的属性时发现依赖 B → 触发 B 的创建
3. 实例化 B → 生成原始对象 B,并将 B 的 ObjectFactory 存入三级缓存
4. 注入 B 的属性时发现依赖 A → 从三级缓存获取 A 的早期引用(可能经过 AOP 代理)
5. B 完成属性注入和初始化 → 存入一级缓存
6. A 继续注入 B → 完成 A 的初始化 → 存入一级缓存
  • 关键点:三级缓存(singletonFactories)通过 ObjectFactory 延迟生成早期引用,确保即使 Bean 被代理(如事务管理),也能正确注入。

三、流程图解

开始创建 Bean A
实例化 A, 存入三级缓存
注入 A 属性时发现依赖 B
开始创建 Bean B
实例化 B, 存入三级缓存
注入 B 属性时发现依赖 A
从三级缓存获取 A 的早期引用
B 完成初始化, 存入一级缓存
A 完成属性注入
A 完成初始化, 存入一级缓存

四、注意事项与限制

  1. 仅支持单例模式:原型 Bean 的循环依赖无法解决。
  2. AOP 代理的影响:若循环依赖的 Bean 需要动态代理(如事务管理),三级缓存中的 ObjectFactory 会提前生成代理对象。
  3. Spring Boot 2.6+ 的默认限制
    默认关闭循环依赖支持,需通过配置 spring.main.allow-circular-references=true 开启。

五、源码核心逻辑

// AbstractAutowireCapableBeanFactory 中的 doCreateBean 方法
protected Object doCreateBean(...) {
    // 1. 实例化 Bean
    instanceWrapper = createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();

    // 2. 将 Bean 的 ObjectFactory 加入三级缓存
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    // 3. 属性注入(可能触发依赖 Bean 的创建)
    populateBean(beanName, mbd, instanceWrapper);

    // 4. 初始化(执行 @PostConstruct 等方法)
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}
  • 关键方法addSingletonFactory 将对象工厂加入三级缓存,getEarlyBeanReference 处理 AOP 代理。

六、常见误区澄清

  • 误区 1:只有 Setter 注入能解决循环依赖。
    事实:字段注入(@Autowired)同样适用,关键在于注入方式是否为属性注入。
  • 误区 2:三级缓存是为了提高性能。
    事实:三级缓存的核心作用是处理 AOP 代理对象的循环依赖,而非性能优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值