Spring 如何解决循环依赖问题?

Spring 通过 三级缓存机制 解决了 单例 Bean 的循环依赖问题,但无法处理 构造器注入的循环依赖。其核心思想是 提前暴露半成品 Bean 的引用,并通过 工厂对象(ObjectFactory) 动态生成代理对象,从而避免循环链条的无限递归。


🧠 一、Spring 循环依赖的定义与限制

✅ 1. 循环依赖的定义

当两个或多个 Bean 相互依赖,形成一个闭环,称为 循环依赖(Circular Dependency)

示例:
@Component
class A {
    @Autowired
    private B b;
}

@Component
class B {
    @Autowired
    private A a;
}

✅ 2. Spring 能解决的循环依赖类型

类型是否支持说明
字段/Setter 注入支持,使用三级缓存机制
构造器注入不支持,构造器注入需在实例化时完成依赖注入
多例(prototype)Bean不支持,Spring 无法管理多例 Bean 的生命周期

🔄 二、Spring 三级缓存机制详解

Spring 使用 三级缓存 来管理单例 Bean 的创建过程,确保在依赖注入时能获取到“早期引用”,从而打破循环依赖。

📦 1. 三级缓存的结构

Spring 使用 DefaultSingletonBeanRegistry 类中的三个 Map 实现三级缓存:

缓存层级类型作用
一级缓存(singletonObjects)Map<String, Object>存放完全初始化完成的 Bean(可直接使用)
二级缓存(earlySingletonObjects)Map<String, Object>存放提前暴露的“半成品 Bean”(已实例化,但未完成属性注入和初始化)
三级缓存(singletonFactories)Map<String, ObjectFactory<?>>存放生成 Bean 的工厂对象(用于生成代理对象)

🧱 三、Spring 创建 Bean 的流程与三级缓存的协作

Spring 创建单例 Bean 的核心流程如下:

1. 实例化 Bean(构造函数调用)
2. 将 Bean 工厂对象加入三级缓存(singletonFactories)
3. 填充属性(依赖注入)
   - 如果依赖的 Bean 尚未创建,则触发其创建流程
4. 初始化 Bean(调用 Aware、BeanPostProcessor、初始化方法)
5. 将 Bean 升级到一级缓存(singletonObjects)

✅ 1. 实例化 Bean

调用构造函数创建 Bean 实例,此时 Bean 是“半成品”,还未注入属性和初始化。

// AbstractAutowireCapableBeanFactory.java
protected Object createBeanInstance(...) {
    return getInstantiationStrategy().instantiate(...);
}

✅ 2. 将 Bean 工厂对象加入三级缓存

在实例化后,Spring 会将一个 ObjectFactory 放入三级缓存中,用于后续代理对象的生成。

// DefaultSingletonBeanRegistry.java
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    if (!this.singletonObjects.containsKey(beanName)) {
        this.singletonFactories.put(beanName, singletonFactory); // 放入三级缓存
    }
}

✅ 3. 填充属性(依赖注入)

在填充属性时,如果发现某个依赖的 Bean 还未创建,则会递归调用 getBean() 方法,尝试获取该 Bean。

// AbstractAutowireCapableBeanFactory.java
protected void populateBean(...) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            PropertyValues pvsToUse = ((InstantiationAwareBeanPostProcessor) bp).postProcessProperties(...);
            applyPropertyValues(beanName, mbd, bw, pvsToUse);
        }
    }
}
如果依赖的 Bean 还未完全初始化:
  • Spring 会调用 getSingleton(String beanName) 方法,尝试从缓存中获取 Bean。
  • 如果一级缓存没有,就尝试从二级缓存获取。
  • 如果二级缓存也没有,就从三级缓存中获取 ObjectFactory,调用 getObject() 生成 Bean 引用。
// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject(); // 生成早期引用
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

✅ 4. 初始化 Bean(调用 Aware、BeanPostProcessor、初始化方法)

初始化阶段包括:

  • 调用 BeanNameAwareBeanFactoryAware 等接口
  • 执行 BeanPostProcessor.postProcessBeforeInitialization()
  • 调用 @PostConstructInitializingBean.afterPropertiesSet()、自定义 init-method
  • 执行 BeanPostProcessor.postProcessAfterInitialization()
// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(...) {
    invokeAwareMethods(beanName, wrappedBean); // 调用 Aware 接口
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); // 前置处理器
    invokeInitMethods(beanName, wrappedBean, mbd); // 初始化方法
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); // 后置处理器
    return wrappedBean;
}

✅ 5. 将 Bean 升级到一级缓存

在 Bean 完全初始化完成后,将其从三级缓存中移除,放入一级缓存中,供后续使用。

// DefaultSingletonBeanRegistry.java
protected void addSingleton(String beanName, Object singletonObject) {
    this.singletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
    this.earlySingletonObjects.remove(beanName);
}

🧩 四、AOP 代理的处理(结合三级缓存)

如果 Bean 需要 AOP 代理,Spring 会在 BeanPostProcessor.postProcessAfterInitialization() 中生成代理对象。

public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean != null && !containsSingletonCandidate(beanName)) {
        return wrapIfNecessary(bean, beanName, new NullSourceClass());
    }
    return bean;
}

生成代理的过程会调用 AbstractAutoProxyCreator.getProxy(),最终生成一个代理对象。

这个代理对象会被缓存到 三级缓存 中,供其他 Bean 依赖注入时使用。


❗ 五、Spring 解决循环依赖的前提条件

条件说明
单例 BeanSpring 只能解决单例作用域 Bean 的循环依赖
Setter/字段注入支持,因为依赖注入发生在属性填充阶段
构造器注入❌ 不支持,构造器注入需在实例化时完成依赖注入
非代理对象如果 Bean 需要 AOP 代理,代理对象必须通过 ObjectFactory 提前生成

✅ 六、总结

特性Spring 三级缓存机制
解决类型单例 Bean、Setter/字段注入的循环依赖
不支持类型构造器注入、多例 Bean
核心机制提前暴露 Bean 的早期引用(半成品 Bean)
三级缓存结构singletonObjects(一级)、earlySingletonObjects(二级)、singletonFactories(三级)
缓存协作流程三级 → 二级 → 一级
AOP 代理处理通过 ObjectFactory 动态生成代理对象
核心类DefaultSingletonBeanRegistryAbstractAutowireCapableBeanFactory

🧪 七、完整流程图(简化版)

createBean(A)
 └── 实例化 A(构造函数)
 └── addSingletonFactory(A, ObjectFactory)
 └── populateBean(A)
      └── 依赖注入 b → getBean(B)
           └── createBean(B)
                └── 实例化 B(构造函数)
                └── addSingletonFactory(B, ObjectFactory)
                └── populateBean(B)
                     └── 依赖注入 a → getBean(A)
                          └── getSingleton(A) → 从三级缓存获取 A 的早期引用
                └── initializeBean(B)
                     └── postProcessAfterInitialization() → AOP 代理(如有)
                └── addSingleton(B, proxyB)
      └── initializeBean(A)
           └── postProcessAfterInitialization() → AOP 代理(如有)
      └── addSingleton(A, proxyA)

<think>好的,我现在需要回答用户关于Spring框架如何解决循环依赖问题。用户已经提供了一些引用内容,我需要结合这些信息来构建一个结构清晰的中文回答。 首先,我需要明确循环依赖的定义,以及Spring中常见的场景。根据引用[1],单例模式下通过setter注入的循环依赖可以被自动解决,而构造器注入的则不行,需要@Lazy注解。引用[4]提到一级缓存无法解决问题,需要三级缓存。引用[3]详细说明三级缓存机制,包括各个缓存的作用。 接下来,我应该分步骤解释Spring解决方法。首先是三级缓存的结构:一级缓存存放完整Bean,二级缓存存放早期暴露对象,三级缓存存放工厂对象。然后是创建Bean的流程,包括实例化、属性填充、初始化等阶段。需要强调在属性填充时,如果发现依赖的Bean未创建,会触发依赖Bean的创建,而通过提前暴露对象引用,避免无限循环。 还要注意区分构造器注入和setter注入的不同处理方式。根据引用[1],构造器注入无法自动解决,必须用@Lazy延迟加载。这部分需要单独说明。 最后,可能需要提到适用场景和局限性,比如只适用于单例Bean,原型Bean无法解决。同时,结合引用[3]中的优势,说明三级缓存的必要性,如避免重复创建代理对象等。 现在需要组织这些信息,确保逻辑连贯,使用用户要求的格式,如行内公式用$...$,独立公式用$$...$$,但可能此处公式较少。需要生成相关问题,并正确添加引用标识。确保回答真实可靠,结构清晰,分步骤说明。</think>### Spring框架解决循环依赖的方法 Spring通过**三级缓存机制**和**提前暴露对象引用**的方法解决单例Bean的循环依赖问题,核心流程如下: #### 1. 三级缓存结构 - **一级缓存(singletonObjects)**:存储完全初始化后的单例Bean,可直接使用[^4]。 - **二级缓存(earlySingletonObjects)**:存储已实例化但未完成属性注入和初始化的早期对象。 - **三级缓存(singletonFactories)**:存储Bean的工厂对象,用于生成早期对象的引用[^3]。 #### 2. 解决流程(以A依赖B,B依赖A为例) 1. **创建Bean A** - 实例化A对象(调用构造函数),此时A未完成属性注入,称为“早期对象”。 - 将A的工厂对象存入三级缓存(`singletonFactories`)[^4]。 2. **填充A的属性** - 发现A依赖B,尝试从缓存获取B。若B不存在,触发B的创建流程。 3. **创建Bean B** - 实例化B对象,并将B的工厂对象存入三级缓存。 - 填充B的属性时,发现B依赖A,尝试从缓存获取A: - 从三级缓存中获取A的工厂对象,生成A的早期对象引用,存入二级缓存[^3]。 - B完成属性注入和初始化,存入一级缓存。 4. **继续完成A的初始化** - 从一级缓存获取已初始化的B,注入到A中。 - A完成初始化后,从二级缓存移除,存入一级缓存[^4]。 #### 3. 关键机制 - **提前暴露引用**:在实例化后立即暴露对象引用(通过工厂对象),避免无限递归。 - **代理对象处理**:若Bean需要AOP代理,工厂对象会返回代理后的对象,确保依赖一致性[^3]。 #### 4. 限制与例外 - **仅支持单例模式**:原型(Prototype)作用域的Bean无法解决循环依赖。 - **构造器注入问题**:若循环依赖通过构造器注入,Spring无法自动解决,需使用`@Lazy`延迟加载[^1]。 ```java // 构造器注入循环依赖解决示例 @Component public class A { private final B b; public A(@Lazy B b) { // 使用@Lazy延迟初始化 this.b = b; } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值