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 缓存中。

目录
相关文章
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
1153 22
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
3064 0
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1773 13
|
监控 安全 Java
解决 Spring Boot 中 SecurityConfig 循环依赖问题的详解
本文详细解析了在 Spring Boot 中配置 `SecurityConfig` 时可能遇到的循环依赖问题。通过分析错误日志与代码,指出问题根源在于 `SecurityConfig` 类中不当的依赖注入方式。文章提供了多种解决方案:移除 `configureGlobal` 方法、定义 `DaoAuthenticationProvider` Bean、使用构造函数注入以及分离配置类等。此外,还讨论了 `@Lazy` 注解和允许循环引用的临时手段,并强调重构以避免循环依赖的重要性。通过合理设计 Bean 依赖关系,可确保应用稳定启动并提升代码可维护性。
1119 0
|
11月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
11月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
847 2
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
1318 1
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
532 0
|
前端开发 Java 数据库连接
Spring核心原理剖析与解说
每个部分都是将一种巨大并且复杂的技术理念传达为更易于使用的接口,而这就是Spring的价值所在,它能让你专注于开发你的应用,而不必从头开始设计每一部分。
420 32
|
Java 开发者 Spring
Spring框架 - 深度揭秘Spring框架的基础架构与工作原理
所以,当你进入这个Spring的世界,看似一片混乱,但细看之下,你会发现这里有个牢固的结构支撑,一切皆有可能。不论你要建设的是一座宏大的城堡,还是个小巧的花园,只要你的工具箱里有Spring,你就能轻松搞定。
587 9