java全家桶之60: IoC 如何解决循环依赖

Spring IoC 如何解决循环依赖

循环依赖是指两个或多个 Bean 相互依赖,形成闭环的情况(如 A 依赖 B,B 又依赖 A)。Spring IoC 容器通过巧妙的机制解决了这个问题。

解决循环依赖的核心机制

Spring 主要通过 三级缓存 来解决循环依赖问题:

  1. 一级缓存(singletonObjects):存储完全初始化好的单例 Bean

  2. 二级缓存(earlySingletonObjects):存储提前暴露的原始 Bean(未填充属性)

  3. 三级缓存(singletonFactories):存储 Bean 的 ObjectFactory,用于生成原始 Bean 的代理对象

解决流程示例

以 A 依赖 B,B 依赖 A 为例:

  1. 创建 A 的实例

    • 调用 A 的构造器创建原始对象

    • 将 A 的 ObjectFactory 放入三级缓存

    • 开始填充 A 的属性

  2. 发现 A 依赖 B

    • 尝试获取 B 的实例

    • 如果 B 不存在,开始创建 B

  3. 创建 B 的实例

    • 调用 B 的构造器创建原始对象

    • 将 B 的 ObjectFactory 放入三级缓存

    • 开始填充 B 的属性

  4. 发现 B 依赖 A

    • 尝试获取 A 的实例

    • 从三级缓存中找到 A 的 ObjectFactory

    • 通过 ObjectFactory 获取 A 的早期引用(可能是原始对象或代理对象)

    • 将 A 的早期引用放入二级缓存,并从三级缓存移除

  5. B 完成初始化

    • B 获得 A 的早期引用后完成属性注入

    • 将初始化完成的 B 放入一级缓存

  6. A 完成初始化

    • A 获得初始化完成的 B 后完成属性注入

    • 将初始化完成的 A 放入一级缓存

    • 从二级缓存移除 A

解决循环依赖的限制

Spring 只能解决 单例作用域 且 通过属性注入 的循环依赖,以下情况无法解决:

  1. 构造器注入 导致的循环依赖

    • 因为构造器注入需要在实例化时就完成依赖注入

    • 此时 Bean 还未创建,无法提前暴露引用

  2. 原型作用域(prototype) 的循环依赖

    • 原型 Bean 每次获取都是新实例

    • Spring 不会缓存原型 Bean 的引用

  3. @Async 等 AOP 代理的特殊情况

    • 某些代理方式可能导致循环依赖解决失败

最佳实践

  1. 尽量避免循环依赖,设计更清晰的依赖关系

  2. 如果必须使用循环依赖,优先使用 setter 注入而非构造器注入

  3. 对于必须使用构造器注入的情况,考虑重构代码结构

Spring 的这种解决方案在保持性能的同时,优雅地处理了大多数常见的循环依赖场景。

package com.gitee.dbapi.testframe;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TestA {
    public void Ta(){
        System.out.println("ta");
    }
    @Autowired
    TestB testB;
}
package com.gitee.dbapi.testframe;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TestB {
    public void Tb(){
        System.out.println("tb");
    }
    @Autowired
    TestA testA;
}

@Autowired
TestA testA;
@Test
public void test010_retSelectByPkey() {

    testA.Ta();

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leijmdas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值