【无标题】

Spring 循环依赖解决方案完全指南

一、什么是循环依赖

循环依赖指的是两个或多个 Bean 之间相互依赖,形成了一个闭环。例如:

  • A 依赖 B,B 依赖 A
  • A 依赖 B,B 依赖 C,C 依赖 A

二、Spring 解决方案的核心要素

1. 三级缓存

// 一级缓存:存储完全初始化的 bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存:存储早期的 bean 引用
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

// 三级缓存:存储 bean 的工厂对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

2. 创建中标记集合

// 记录正在创建中的 bean
private final Set<String> singletonsCurrentlyInCreation = 
    Collections.newSetFromMap(new ConcurrentHashMap<>(16));

三、解决循环依赖的完整流程

1. Bean 创建开始

protected void beforeSingletonCreation(String beanName) {
    // 将 bean 加入创建中标记集合
    if (!this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

2. 实例化 Bean

  • 创建空对象(仅调用构造方法)
  • 将对象工厂放入三级缓存

3. 属性注入时发现循环依赖

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先查一级缓存
    Object singletonObject = this.singletonObjects.get(beanName);
    
    // 如果一级缓存没有,且当前 bean 正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 查二级缓存
        singletonObject = this.earlySingletonObjects.get(beanName);
        
        // 如果二级缓存也没有,尝试三级缓存
        if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> factory = this.singletonFactories.get(beanName);
            if (factory != null) {
                // 从三级缓存获取早期引用
                singletonObject = factory.getObject();
                // 放入二级缓存
                this.earlySingletonObjects.put(beanName, singletonObject);
                // 从三级缓存移除
                this.singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

四、循环依赖的判定过程

1. 标记判定

  • 通过 singletonsCurrentlyInCreation 集合判断 Bean 是否在创建中
  • 当发现要获取的 Bean 在创建中时,说明可能存在循环依赖

2. 处理流程

  1. A 创建时,将 A 加入 singletonsCurrentlyInCreation
  2. A 注入属性时发现依赖 B,开始创建 B
  3. B 创建时,将 B 加入 singletonsCurrentlyInCreation
  4. B 注入属性时发现依赖 A
  5. 发现 A 在 singletonsCurrentlyInCreation 中,确认存在循环依赖
  6. 尝试从三级缓存获取 A 的早期引用

五、缓存的移除时机

1. Bean 完全初始化后

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 放入一级缓存
        this.singletonObjects.put(beanName, singletonObject);
        // 从三级缓存移除
        this.singletonFactories.remove(beanName);
        // 从二级缓存移除
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

2. Bean 创建失败时

protected void removeSingleton(String beanName) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.remove(beanName);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.remove(beanName);
    }
}

六、关键点总结

1. singletonsCurrentlyInCreation 的作用

  • 是循环依赖检测的核心
  • 记录 Bean 的创建状态
  • 用于及时发现循环依赖问题

2. 三级缓存的配合

  • 三级缓存存储工厂对象,支持 AOP 代理
  • 二级缓存存储早期引用,避免重复创建
  • 一级缓存存储完整对象,最终使用

3. 局限性

  • 只能解决 setter 注入的循环依赖
  • 只能解决单例 Bean 的循环依赖
  • 不能解决构造器注入的循环依赖

七、最佳实践

  1. 尽量避免循环依赖的设计
  2. 使用 @Lazy 注解处理特殊场景
  3. 优先使用构造器注入,更容易发现循环依赖问题
  4. 合理设计类的依赖关系,避免复杂的依赖环
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值