Spring框架解决循环依赖

目录

Spring框架解决循环依赖

1. 循环依赖的类型

2. 三级缓存解决机制

3. 解决流程

4. 关键源码分析

5. 为什么构造器注入无法解决循环依赖

6. 实际开发中的建议


Spring框架解决循环依赖

循环依赖是指两个或多个Bean相互依赖,形成闭环的情况(如A依赖B,B又依赖A)。Spring框架通过巧妙的设计解决了部分循环依赖问题,下面解析其实现原理。

1. 循环依赖的类型

Spring能解决的循环依赖主要是单例Bean通过setter/字段注入形成的循环依赖,以下情况无法解决:

  • 构造器注入形成的循环依赖
  • 原型(prototype)作用域的Bean循环依赖
  • 通过@PostConstruct方法形成的依赖

2. 三级缓存解决机制

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

缓存级别

名称

存储内容

作用阶段

第一级缓存

singletonObjects

完全初始化好的单例Bean

成品Bean,可供使用

第二级缓存

earlySingletonObjects

早期引用(原始对象或代理对象)

解决循环依赖

第三级缓存

singletonFactories

ObjectFactory(用于生成代理对象)

创建代理对象的工厂

3. 解决流程

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

  1. 开始创建A
  • 实例化A(调用构造器),得到一个原始对象
  • 将A的ObjectFactory放入三级缓存(singletonFactories)
  • 开始填充A的属性(依赖注入阶段)
  1. 发现A依赖B
  • 从容器中查找B
  • 如果B不存在,开始创建B
  1. 开始创建B
  • 实例化B(调用构造器),得到一个原始对象
  • 将B的ObjectFactory放入三级缓存
  • 开始填充B的属性
  1. 发现B依赖A
  • 从容器中查找A:
    • 一级缓存没有(未完全初始化)
    • 检查二级缓存没有
    • 从三级缓存获取A的ObjectFactory并执行,得到A的早期引用(可能是代理对象)
    • 将A的早期引用放入二级缓存,从三级缓存移除
  • 将A的早期引用注入到B
  • B完成属性填充,初始化,放入一级缓存
  1. 回到A的创建
  • 从一级缓存获取到已完全初始化的B
  • 将B注入到A
  • A完成属性填充,初始化
  • 将A放入一级缓存,从二级缓存移除

4. 关键源码分析

DefaultSingletonBeanRegistry类中的核心方法:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 从一级缓存查询
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 从二级缓存查询
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3. 从三级缓存获取ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 执行ObjectFactory获取早期引用
                    singletonObject = singletonFactory.getObject();
                    // 放入二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

5. 为什么构造器注入无法解决循环依赖

构造器注入的循环依赖无法解决是因为:

在构造器调用时,Bean还未创建完成,无法放入三级缓存

没有提前暴露对象引用的机会

Spring的设计选择:宁愿在启动时失败,也不愿在运行时出现不可预知的行为

6. 实际开发中的建议

  1. 避免循环依赖
  • 重新设计组件结构
  • 使用事件驱动或观察者模式解耦
  1. 必须使用时
  • 使用setter/字段注入而非构造器注入
  • 确保是单例Bean
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值