Spring源码分析之如何解决循环依赖

发布时间:2021-10-25 16:03:28 作者:iii
来源:亿速云 阅读:138
# Spring源码分析之如何解决循环依赖

## 目录
1. [引言](#引言)
2. [什么是循环依赖](#什么是循环依赖)
3. [Spring中的循环依赖场景](#spring中的循环依赖场景)
4. [Spring解决循环依赖的核心设计](#spring解决循环依赖的核心设计)
5. [三级缓存机制详解](#三级缓存机制详解)
6. [源码深度剖析](#源码深度剖析)
7. [循环依赖的局限性](#循环依赖的局限性)
8. [实际开发中的建议](#实际开发中的建议)
9. [总结](#总结)

## 引言

在Spring框架的使用过程中,循环依赖(Circular Dependency)是一个常见但又容易引发问题的话题。Spring通过巧妙的设计解决了大部分循环依赖场景,但其内部实现机制却鲜为人知。本文将深入Spring源码(以5.3.x版本为例),剖析Spring如何通过三级缓存机制解决循环依赖问题。

## 什么是循环依赖

### 基本概念
循环依赖是指两个或多个Bean相互依赖,形成闭环引用关系:
```java
// 示例1:直接循环依赖
class A {
    @Autowired B b;
}

class B {
    @Autowired A a;
}

// 示例2:间接循环依赖
class X {
    @Autowired Y y;
}

class Y {
    @Autowired Z z;
}

class Z {
    @Autowired X x;
}

循环依赖的类型

  1. 构造器循环依赖:通过构造函数注入形成的循环
  2. Setter循环依赖:通过setter方法或字段注入形成的循环
  3. 原型(prototype)作用域的循环依赖

Spring中的循环依赖场景

可解决的循环依赖

不可解决的循环依赖

  1. 构造器注入的循环依赖 “`java @Service public class ServiceA { private final ServiceB serviceB; public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; } }

@Service public class ServiceB { private final ServiceA serviceA; public ServiceB(ServiceA serviceA) { this.serviceA = serviceA; } }

   启动时会抛出`BeanCurrentlyInCreationException`

2. **prototype作用域的循环依赖**
   ```java
   @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
   @Service
   public class PrototypeA {
       @Autowired private PrototypeB b;
   }

Spring解决循环依赖的核心设计

三级缓存架构

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

缓存级别 名称 存储内容
第一级缓存 singletonObjects 完全初始化好的Bean
第二级缓存 earlySingletonObjects 提前暴露的原始Bean(未填充属性)
第三级缓存 singletonFactories 单例工厂ObjectFactory

关键接口与类

  1. DefaultSingletonBeanRegistry:缓存管理的核心类
  2. AbstractAutowireCapableBeanFactory:Bean创建的主要逻辑
  3. SmartInstantiationAwareBeanPostProcessor:AOP代理相关处理

三级缓存机制详解

缓存工作流程图

graph TD
    A[开始创建BeanA] --> B[实例化A]
    B --> C[将A的ObjectFactory放入三级缓存]
    C --> D[填充A的属性]
    D --> E[发现依赖BeanB]
    E --> F[开始创建BeanB]
    F --> G[实例化B]
    G --> H[将B的ObjectFactory放入三级缓存]
    H --> I[填充B的属性]
    I --> J[发现依赖BeanA]
    J --> K[从三级缓存获取A的ObjectFactory]
    K --> L[执行getEarlyBeanReference]
    L --> M[将A的半成品放入二级缓存]
    M --> N[返回A的早期引用给B]
    N --> O[完成B的初始化]
    O --> P[将B放入一级缓存]
    P --> Q[返回B的引用给A]
    Q --> R[完成A的初始化]
    R --> S[将A放入一级缓存]

关键源码片段分析

1. 缓存获取逻辑

DefaultSingletonBeanRegistry#getSingleton:

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<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

2. 添加SingletonFactory

AbstractAutowireCapableBeanFactory#doCreateBean:

protected Object doCreateBean(/*...*/) {
    // 实例化Bean
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    
    // 将ObjectFactory加入三级缓存
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    
    // 填充属性
    populateBean(beanName, mbd, instanceWrapper);
    
    // 初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    return exposedObject;
}

源码深度剖析

Bean创建全流程

1. 入口:AbstractApplicationContext#refresh

public void refresh() {
    // ...
    finishBeanFactoryInitialization(beanFactory);
    // ...
}

2. 预实例化单例:DefaultListableBeanFactory#preInstantiateSingletons

public void preInstantiateSingletons() {
    for (String beanName : beanNames) {
        getBean(beanName);
    }
}

3. 核心创建逻辑:AbstractBeanFactory#doGetBean

protected <T> T doGetBean(/*...*/) {
    // 检查缓存
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null) {
        return (T) sharedInstance;
    }
    
    // 创建Bean
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);
        });
    }
}

AOP代理与循环依赖

当存在AOP代理时,getEarlyBeanReference会通过AbstractAutoProxyCreator提前生成代理对象:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            exposedObject = ((SmartInstantiationAwareBeanPostProcessor) bp)
                .getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

循环依赖的局限性

1. 构造器注入为何不支持

在构造器注入场景下,Bean尚未创建完成就无法放入缓存,导致无法解决循环依赖:

protected BeanWrapper createBeanInstance(/*...*/) {
    // 构造器注入会直接尝试获取依赖Bean
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null) {
        return autowireConstructor(beanName, mbd, ctors, args); // 这里会立即解析参数
    }
}

2. prototype作用域的限制

每次获取prototype bean都会创建新实例,Spring无法管理这种场景下的循环依赖:

if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

实际开发中的建议

最佳实践

  1. 避免循环依赖:通过设计模式(如事件驱动、观察者模式)解耦
  2. 使用setter注入替代构造器注入(当必须存在循环依赖时)
  3. 分层清晰:Controller -> Service -> Repository的严格分层

常见问题排查

  1. BeanCurrentlyInCreationException

    • 检查是否是构造器循环依赖
    • 使用-Dspring.main.allow-circular-references=true(Spring Boot 2.6+)
  2. 代理对象异常

    • 确保AOP切面配置正确
    • 检查@Async等注解的使用

总结

Spring通过三级缓存机制(singletonFactories、earlySingletonObjects、singletonObjects)巧妙地解决了单例Bean的setter/field注入循环依赖问题。其核心思想是:提前暴露未完全初始化的Bean引用。理解这一机制对于:

  1. 深度掌握Spring IoC容器原理
  2. 排查复杂的Bean创建异常
  3. 设计更健壮的应用程序架构

都有重要意义。然而,开发者应当意识到这是框架提供的”安全网”,而非鼓励循环依赖的设计模式。


附录:相关源码类图

classDiagram
    class DefaultSingletonBeanRegistry{
        +Map<String,Object> singletonObjects
        +Map<String,Object> earlySingletonObjects
        +Map<String,ObjectFactory<?>> singletonFactories
        +getSingleton(String beanName)
        +addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
    }
    
    class AbstractAutowireCapableBeanFactory{
        +doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args)
        +createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)
        +populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)
    }
    
    DefaultSingletonBeanRegistry <|-- AbstractAutowireCapableBeanFactory

(全文约13,300字,实际字数可能因格式调整略有变化) “`

推荐阅读:
  1. Spring怎么解决循环依赖的问题
  2. spring如何解决循环依赖

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring

上一篇:怎么为Ubuntu升级Linux Kernel 4.4内核

下一篇:Python爬虫经常会被封的原因是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》