Spring循环依赖底层实现原理深度剖析

简介: 在 Spring 中,循环依赖(Circular Dependency)指的是两个或多个 Bean 之间相互依赖,形成了一个循环依赖的关系。当出现循环依赖时,Spring 需要通过一些特殊的技术手段来解决这个问题,保证 Bean 的正确创建和初始化。下面我们一起通过理论再结合源码一起推导出spring循环依赖底层真想。达到对Spring 循环依赖的底层实现原理的深度剖析。1. 循环依赖的问题循环依赖问题的出现原因是因为在创建 Bean 的过程中,Bean 之间相互依赖,导致 Bean 的创建顺序不确定,从而无法保证所有的 Bean 都被正确地创建和初始化。例如,假设有两个 Bean A

在 Spring 中,循环依赖(Circular Dependency)指的是两个或多个 Bean 之间相互依赖,形成了一个循环依赖的关系。当出现循环依赖时,Spring 需要通过一些特殊的技术手段来解决这个问题,保证 Bean 的正确创建和初始化。下面我们一起通过理论再结合源码一起推导出spring循环依赖底层真想。达到对Spring 循环依赖的底层实现原理的深度剖析。

1. 循环依赖的问题

循环依赖问题的出现原因是因为在创建 Bean 的过程中,Bean 之间相互依赖,导致 Bean 的创建顺序不确定,从而无法保证所有的 Bean 都被正确地创建和初始化。例如,假设有两个 Bean A 和 B,它们都需要依赖对方才能完成初始化:

  • 我们先通过一个简单示例引出问题
@Component
public class A {
    private B b;
    public A(B b) {
        this.b = b;
    }
}
@Component
public class B {
    private A a;
    public B(A a) {
        this.a = a;
    }
}
复制代码

在这种情况下,如果直接使用 Spring 的标准依赖注入方式,创建 A 和 B 的时候会出现循环依赖的问题,导致程序无法正确运行。

2. 解决循环依赖的方法

Spring 解决循环依赖的方法是通过提前暴露半成品对象(Early-Stage Object)来解决。当 Spring 创建一个 Bean 的时候,它会先创建该 Bean 的半成品对象,然后再注入该 Bean 所依赖的其他 Bean。当所有的 Bean 都被创建并注入完成后,Spring 再完成这些半成品对象的初始化,从而解决了循环依赖的问题。


Spring 解决循环依赖的过程大致分为三个步骤:


创建 Bean 的半成品对象,并将其添加到缓存中。

注入该 Bean 所依赖的其他 Bean。

完成 Bean 的初始化,将半成品对象转换为完整的 Bean 对象。

下面是 Spring 解决循环依赖的具体实现原理。

3. Spring 循环依赖的实现原理

Spring 的循环依赖解决方案主要依赖于两个技术:BeanPostProcessor三级缓存

BeanPostProcessor

BeanPostProcessor 是 Spring 中的一个接口,它提供了两个方法:postProcessBeforeInitialization 和 postProcessAfterInitialization。这两个方法分别在 Bean 的初始化前后被调用,可以用来对 Bean 进行定制化处理。


在解决循环依赖问题时,Spring 使用 BeanPostProcessor 在 Bean 初始化之前对 Bean 进行处理,从而实现提前暴露半成品对象的目的。

- 三级缓存

Spring 中的 BeanFactory 是一个三级缓存结构,其中包含了singletonObjects、earlySingletonObjects 和 singletonFactories 三个缓存。


当 Spring 创建一个 Bean 的时候,它会先检查 singletonObjects 缓存中是否存在该 Bean 的实例。如果存在,直接返回该实例;否则继续创建该 Bean 的实例。


如果在创建该 Bean 的过程中出现了循环依赖,Spring 会将该 Bean 的半成品对象存储在 earlySingletonObjects 缓存中,并将其标记为“当前正在创建的 Bean”,然后继续创建该 Bean 所依赖的其他 Bean。当所有的 Bean 都被创建完成后,Spring 会调用 BeanPostProcessor 的 postProcessAfterInitialization 方法,将所有标记为“当前正在创建的 Bean”的半成品对象转化为完整的 Bean 对象,并存储在 singletonObjects 缓存中。


如果在创建该 Bean 的过程中需要调用其他 Bean 的工厂方法,则 Spring会将该 Bean 的工厂方法存储在 singletonFactories 缓存中,以便在创建其他 Bean 时使用。当所有的 Bean 都被创建完成后,Spring 会遍历 singletonFactories 缓存中的所有工厂方法,调用它们的 getObject() 方法,将其转换为完整的 Bean 对象,并存储在 singletonObjects 缓存中。


通过使用三级缓存和 BeanPostProcessor,Spring 能够在 Bean 的创建过程中解决循环依赖问题,并保证所有的 Bean 都被正确地创建和初始化。

4. 循环依赖的限制

虽然 Spring 能够解决循环依赖问题,但是它也有一些限制:


循环依赖只适用于 singleton 作用域的 Bean。对于 prototype 作用域的 Bean,Spring 无法解决循环依赖问题。

循环依赖只适用于 constructor 和 setter 注入方式。对于其他的注入方式,如字段注入或方法注入,Spring 无法解决循环依赖问题。

总之,Spring 的循环依赖解决方案是通过 BeanPostProcessor 和三级缓存实现的。当出现循环依赖时,Spring 会先创建 Bean 的半成品对象,并将其添加到 earlySingletonObjects 缓存中,然后继续创建该 Bean 所依赖的其他 Bean。当所有的 Bean 都被创建完成后,Spring 会调用 BeanPostProcessor 的 postProcessAfterInitialization 方法,将所有标记为“当前正在创建的 Bean”的半成品对象转化为完整的 Bean 对象,并存储在 singletonObjects 缓存中。虽然 Spring 能够解决循环依赖问题,但是它也有一些限制,需要在使用时注意。


此外,需要注意的是,循环依赖可能会导致性能问题。当存在大量的循环依赖关系时,Spring 需要创建大量的半成品对象和缓存,从而占用大量的内存和 CPU 资源。因此,在设计应用程序时,需要尽量避免循环依赖的出现。


最后,如果出现循环依赖的问题,建议通过重构代码的方式来解决,尽量减少 Bean 之间的相互依赖关系。如果无法避免循环依赖,可以考虑使用其他依赖注入框架,或者手动管理 Bean 的创建和初始化过程,以避免循环依赖的问题。


好的,下面我们来看一下 Spring 源码中是如何解决循环依赖的问题的。

5.源码解析

好的,下面是对 Spring 源码中解决循环依赖问题的关键部分进行注释的代码:

// AbstractApplicationContext.java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext, DisposableBean {
    public void refresh() throws BeansException, IllegalStateException {
        // 创建 BeanFactory,并通过 BeanFactoryPostProcessor 对 BeanFactory 进行处理
        refreshBeanFactory();
        // 通过 BeanDefinitionReader 将 Bean 的定义信息读取到 BeanFactory 中,并注册到 BeanDefinitionMap 中
        // 然后,通过 DefaultListableBeanFactory 中的 preInstantiateSingletons() 方法创建 Bean 实例
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // ...
        // 创建 Bean 的过程是由 getBean() 方法触发的
        // 在 getBean() 方法中,会先检查 singletonObjects 缓存中是否存在该 Bean 的实例
        // 如果存在,直接返回该实例;否则继续创建该 Bean 的实例
        beanFactory.getBean(beanName);
        // ...
    }
}
// DefaultListableBeanFactory.java
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
    public Object getBean(String name) throws BeansException {
        // ...
        // 先检查 singletonObjects 缓存中是否存在该 Bean 的实例
        // 如果存在,直接返回该实例;否则继续创建该 Bean 的实例
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null) {
            // ...
            return null;
        }
        // ...
        // 创建该 Bean 的半成品对象,并将其存储在 earlySingletonObjects 缓存中
        // 然后,继续创建该 Bean 所依赖的其他 Bean
        // 当所有的 Bean 都被创建完成后,会调用 BeanPostProcessor 的 postProcessAfterInitialization() 方法
        // 将所有标记为“当前正在创建的 Bean”的半成品对象转化为完整的 Bean 对象,并存储在 singletonObjects 缓存中
        createBean(beanName, mbd, args);
        // ...
    }
    protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
        // ...
        // 创建该 Bean 的半成品对象,并将其存储在 earlySingletonObjects 缓存中
        Object beanInstance = doCreateBean(beanName, mbd, args);
        // ...
        // 调用 BeanPostProcessor 的 postProcessAfterInitialization() 方法
        // 将所有标记为“当前正在创建的 Bean”的半成品对象转化为完整的 Bean 对象,并存储在 singletonObjects 缓存中
        // 在 postProcessAfterInitialization() 方法中,会检查该 Bean 是否存在循环依赖的问题
        Object exposedObject = bean;
        if (mbd.isSingleton()) {
            // ...
            // 将该 Bean 存储在 singletonObjects 缓存中
            addSingleton(beanName, singletonObject);
            // ...
        }
        // ...
    }
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
        // ...
        // 如果创建依赖的 Bean 时发现循环依赖的问题
        // 会先将该 Bean 的工厂方法存储在 singletonFactories 缓存中,以便在创建其他 Bean 时使用
        if (mbd.isPrototype()) {
            // ...
        } else {
            // ...
            // 如果存在循环依赖的问题
            if (isSingletonCurrentlyInCreation(beanName)) {
                // 先从 singletonFactories 缓存中获取该 Bean 的工厂方法
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // ...
                    // 将该 Bean 存储在 singletonObjects 缓存中
                    addSingleton(beanName, singletonObject);
                    // ...
                }
            }
            // ...
        }
        // ...
    }
    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        // ...
        // 在对属性进行赋值的过程中,如果发现循环依赖的问题
        // 会先将该 Bean 的工厂方法存储在 singletonFactories 缓存中
        if (hasInstantiationAwareBeanPostProcessors()) {
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                // 如果该 Bean 存在循环依赖的问题
                // 则先将该 Bean 的工厂方法存储在 singletonFactories 缓存中
                // 然后,将该 Bean 标记为“当前正在创建的 Bean”
                // 最后,调用 BeanPostProcessor 的 postProcessBeforeInstantiation() 方法
                // 创建该 Bean 的工厂方法,并将其存储在 singletonFactories 缓存中
                // 在 postProcessBeforeInstantiation() 方法中,会返回该 Bean 的工厂方法
                // 以便在创建其他 Bean 时使用
                Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    // ...
                    // 将该 Bean 的工厂方法存储在 singletonFactories 缓存中
                    singletonFactories.put(beanName, () -> result);
                    // ...
                    return result;
                }
            }
        }
        // ...
    }
    protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        // ...
        // 在对属性进行赋值的过程中,如果发现循环依赖的问题
        // 会先将该 Bean 的工厂方法存储在 singletonFactories 缓存中
        if (hasInstantiationAwareBeanPostProcessors()) {
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                // ...
                // 如果该 Bean 存在循环依赖的问题
                // 则先将该 Bean 的工厂方法存储在 singletonFactories 缓存中
                // 然后,将该 Bean 标记为“当前正在创建的 Bean”
                // 最后,调用 BeanPostProcessor 的 postProcessPropertyValues() 方法
                // 对该 Bean 的属性进行赋值,并返回新的 PropertyValues 对象
                // 在 postProcessPropertyValues() 方法中,会检查该 Bean 是否存在循环依赖的问题
                PropertyValues pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bean, beanName);
                if (pvsToUse == null) {
                    // ...
                } else {
                    // ...
                    pvs = pvsToUse;
                }
            }
        }
        // ...
    }
}
复制代码

6.总结

在 Spring 中,创建 Bean 的过程是由 AbstractApplicationContext 类中的 refresh() 方法触发的。在 refresh() 方法中,会先创建 BeanFactory,并通过 BeanFactoryPostProcessor 对 BeanFactory 进行处理。然后,通过 BeanDefinitionReader 将 Bean 的定义信息读取到 BeanFactory 中,并注册到 BeanDefinitionMap 中。接着,通过 DefaultListableBeanFactory 中的 preInstantiateSingletons() 方法创建 Bean 实例。


在 DefaultListableBeanFactory 中,Bean 的创建过程是由 getBean() 方法触发的。在 getBean() 方法中,会先检查 singletonObjects 缓存中是否存在该 Bean 的实例。如果存在,直接返回该实例;否则继续创建该 Bean 的实例。在创建该 Bean 的过程中,如果存在循环依赖的问题,会先创建 Bean 的半成品对象,并将其存储在 earlySingletonObjects 缓存中。然后,继续创建该 Bean 所依赖的其他 Bean。当所有的 Bean 都被创建完成后,会调用 SmartInstantiationAwareBeanPostProcessor 的 postProcessAfterInitialization() 方法,将所有标记为“当前正在创建的 Bean”的半成品对象转化为完整的 Bean 对象,并存储在 singletonObjects 缓存中。


在转化半成品对象为完整的 Bean 对象的过程中,Spring 会通过 BeanWrapperImpl 中的 setPropertyValue() 方法为该 Bean 的属性赋值。在对属性进行赋值的过程中,如果存在依赖关系,会调用 getBean() 方法创建依赖的 Bean。如果创建依赖的 Bean 时发现循环依赖的问题,会先从 singletonFactories 缓存中获取该 Bean 的工厂方法,并将其存储在 singletonFactories 缓存中。然后,继续创建该 Bean 所依赖的其他 Bean。当所有的 Bean 都被创建完成后,会遍历 singletonFactories 缓存中的所有工厂方法,调用其 getObject() 方法,将其转换为完整的 Bean 对象,并存储在 singletonObjects 缓存中。


通过源码剖析,我们可以看出,Spring 解决循环依赖问题的核心是通过三级缓存和 BeanPostProcessor 实现的。


在创建 Bean 的过程中,如果出现循环依赖的问题,会先创建 Bean 的半成品对象,并将其存储在 earlySingletonObjects 缓存中。

继续创建该 Bean 所依赖的其他 Bean。当所有的 Bean 都被创建完成后,会调用 BeanPostProcessor 的 postProcessAfterInitialization() 方法,将所有标记为“当前正在创建的 Bean”的半成品对象转化为完整的 Bean 对象,并存储在 singletonObjects 缓存中。

在属性赋值的过程中,如果发现循环依赖的问题,会先将该 Bean 的工厂方法存储在 singletonFactories 缓存中,以便在创建其他 Bean 时使用。当所有的 Bean 都被创建完成后,会遍历 singletonFactories 缓存中的所有工厂方法,调用其 getObject() 方法,将其转换为完整的 Bean 对象,并存储在 singletonObjects 缓存中。

目录
相关文章
|
1月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
61 0
|
28天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
66 1
|
28天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
66 0
|
2天前
|
安全 Java API
Spring工厂API与原理
Spring工厂API与原理
23 10
|
8天前
|
存储 缓存 Java
Spring解决循环依赖
Spring解决循环依赖
|
14天前
|
Java Shell 测试技术
一次配置,多场景适用:Spring Boot多套配置文件的深度剖析
一次配置,多场景适用:Spring Boot多套配置文件的深度剖析
31 0
一次配置,多场景适用:Spring Boot多套配置文件的深度剖析
|
25天前
|
缓存 算法 Java
开发必懂的Spring循环依赖图解 Spring 循环依赖
开发必懂的Spring循环依赖图解 Spring 循环依赖
18 1
|
28天前
|
缓存 算法 Java
Spring解决循环依赖
Spring解决循环依赖
19 0
|
29天前
|
XML 缓存 Java
天天用 Spring,bean 实例化原理你懂吗
天天用 Spring,bean 实例化原理你懂吗
17 0
|
8月前
|
缓存 Java Spring
最通俗的方式理解Spring循环依赖三级缓存
有位粉丝找我,说要耽误我5分钟时间,想让我帮助它理解一下Spring循环依赖的三级缓存,绕晕了一个星期,没有想明白。我想今天,用最通俗易懂的方式给大家重新梳理一下,保证让你听懂了。
80 0