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

目录
相关文章
|
7天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
19 0
|
7天前
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
1月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
128 9
|
1月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
36 1
|
1月前
|
设计模式 Java Spring
Spring Boot监听器的底层实现原理
Spring Boot监听器的底层实现原理主要基于观察者模式(也称为发布-订阅模式),这是设计模式中用于实现对象之间一对多依赖的一种常见方式。在Spring Boot中,监听器的实现依赖于Spring框架提供的事件监听机制。
28 1
|
2月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
40 4
|
1月前
|
XML 前端开发 Java
拼多多1面:聊聊Spring MVC的工作原理!
本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖
43 0
|
1月前
|
负载均衡 Java API
Spring Cloud原理详解
Spring Cloud原理详解
69 0
|
1月前
|
负载均衡 Java 网络架构
Spring Cloud原理详解
介绍了Spring Cloud的原理和核心组件,包括服务注册与发现、配置管理、负载均衡、断路器、智能路由、分布式消息传递、分布式追踪和服务熔断等,旨在帮助开发人员快速构建和管理微服务架构中的分布式系统。
56 0
下一篇
无影云桌面