Spring中的循环依赖详解

 更新时间:2023年09月14日 08:47:52   作者:这堆干货有点猛  
这篇文章主要介绍了Spring中的循环依赖详解,  Spring 框架是一个流行的Java应用程序框架,它提供了许多强大的功能,如依赖注入和面向切面编程,然而在使用 Spring 框架时,我们可能会遇到循环依赖的问题,需要的朋友可以参考下

Spring 框架是一个流行的Java应用程序框架,它提供了许多强大的功能,如依赖注入和面向切面编程。然而在使用 Spring 框架时,我们可能会遇到循环依赖的问题。

这种情况发生在两个或多个 Bean 之间相互依赖的情况下,其中一个 Bean 依赖于另一个 Bean,而另一个 Bean 又依赖于第一个 Bean

在这种情况下,Spring 框架需要解决循环依赖的问题,否则应用程序可能会出现死锁或其他错误。

本文将探讨 Spring 框架是如何解决循环依赖的问题,以及它是如何工作的。我们将分析 Spring 框架的源代码,并提供一些示例来说明 Spring 框架如何解决循环依赖的问题。

解决循环依赖的原理

在 Spring 框架中,当两个或多个 Bean 之间相互依赖时, Spring 框架会创建一个代理对象,该代理对象负责管理这些 Bean 之间的依赖关系。这个代理对象被称为“early proxy”或“exposed proxy”。

当一个 Bean 需要访问另一个 Bean 时, Spring 框架会通过代理对象来获取该 Bean。这个代理对象负责保证 Bean 的实例化顺序,确保每个 Bean 都只被实例化一次,并且在所有依赖关系被满足之前,不会暴露任何未实例化的 Bean 。

Spring 框架解决循环依赖的过程如下

  • 当 Spring 框架启动时,它会扫描应用程序中的所有 Bean,并将它们注册到一个 Bean Factory中。
  • 当一个 Bean 被实例化时Spring 框架会检查它是否有任何依赖关系。
  • 如果 Bean 有依赖关系,则 Spring 框架会检查这些依赖关系是否已经被创建。
  • 如果依赖关系已经被创建,则 Spring 框架会将依赖关系注入到 Bean 中,并返回该 Bean 的实例。
  • 如果依赖关系还没有被创建,则 Spring 框架会创建一个代理对象(通过 getEarlyBeanReference 方法),并将该代理对象暴露给该 Bean。
  • 当依赖关系被创建时,Spring 框架会使用代理对象来获取依赖关系,并将依赖关系注入到 Bean 中。
  • 当所有依赖关系都被满足时,Spring 框架会返回该 Bean 的实例。

源码解析

为了更好地理解 Spring 框架如何解决循环依赖的问题,我们将分析 Spring 框架的源代码。

下面是一个简单的示例,演示了 Spring 框架如何解决循环依赖的问题。

public class A {
    private B b;
    public A() {}
    public void setB(B b) {
        this.b = b;
    }
}
public class B {
    private A a;
    public B() {}
    public void setA(A a) {
        this.a = a;
    }
}
@Configuration
public class AppConfig {
    @Bean
    public A a() {
        return new A();
    }
    @Bean
    public B b() {
        return new B();
    }
}

在这个示例中,类A依赖于类B,而类B又依赖于类A。因此,这个示例展示了一个循环依赖的情况。

当应用程序启动时,Spring 框架会扫描所有的 Bean ,并将它们注册到一个BeanFactory中。

当 BeanFactory创建 Bean 时,它会检查 Bean 是否有任何依赖关系。

在这个示例中,当 BeanFactory 创建 A 和 B 时,它会检查它们之间的依赖关系。由于 A 依赖于 B,而 B 又依赖于 A,因此存在循环依赖的问题。

为了解决这个问题,Spring 框架会创建一个代理对象,该代理对象负责管理A和B之间的依赖关系。在这个示例中,当 BeanFactory 创建 A 时,它会创建一个代理对象,并将该代理对象暴露给A。当BeanFactory创建B时,它会创建一个代理对象,并将该代理对象暴露给 B。这样,A和B就可以通过代理对象来访问彼此,而不会出现循环依赖的问题。

下面是 Spring 框架解决循环依赖的源码示例:

首先 Spring 框架会从缓存中获取需要的 bean:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

上面代码中,框架分别从 singletonObjects、earlySingletonObjects、singletonFactories 中获取需要的实例,如果获取不到,就就行创建,在创建的过程中如果发现需要处理循环依赖,就会调用下面方法获取代理对象:

private Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                if (exposedObject == null) {
                    return exposedObject;
                }
            }
        }
    }
    return exposedObject;
}

在这个示例中,getEarlyBeanReference() 方法是 Spring 框架用来获取代理对象的方法。该方法首先检查 Bean 是否为合成的 Bean ,然后检查该 Bean 是否有任何实例化后的 Bean 后处理器。如果 Bean 有实例化后的 Bean 后处理器,则 Spring 框架会使用这些 Bean 后处理器来获取代理对象。

spring.png

总结

在本文中,我们探讨了 Spring 框架是如何解决循环依赖的问题的。

我们分析了 Spring 框架的源代码,并提供了一些示例来说明 Spring 框架如何解决循环依赖的问题。

总之, Spring 框架通过创建代理对象来解决循环依赖的问题,该代理对象负责管理 Bean 之间的依赖关系,并确保每个 Bean 都只被实例化一次。

到此这篇关于Spring中的循环依赖详解的文章就介绍到这了,更多相关Spring循环依赖内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Struts 2中实现Ajax的三种方式

    Struts 2中实现Ajax的三种方式

    这篇文章主要介绍了Struts 2中实现Ajax的三种方式,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-05-05
  • Java三目运算符用法举例

    Java三目运算符用法举例

    三目运算符是我们经常在代码中使用的,这篇文章主要给大家介绍了关于Java三目运算符用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • 详解Java的Hibernate框架中的set映射集与SortedSet映射

    详解Java的Hibernate框架中的set映射集与SortedSet映射

    这篇文章主要介绍了详解Java的Hibernate框架中的set映射集与SortedSet映射,Hibernate是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-12-12
  • spring+apollo动态获取yaml格式的配置方式

    spring+apollo动态获取yaml格式的配置方式

    这篇文章主要介绍了spring+apollo动态获取yaml格式的配置方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-04-04
  • Mybatis源码解析之mapper接口的代理模式详解

    Mybatis源码解析之mapper接口的代理模式详解

    这篇文章主要介绍了Mybatis源码解析之mapper接口的代理模式详解,在mybatis中执行sql时有两种方式,一种是基于statementId,也就是直接调用SqlSession的方法,需要的朋友可以参考下
    2023-12-12
  • SpringBoot中的拦截器使用详解

    SpringBoot中的拦截器使用详解

    本文详细解析了Spring框架中的拦截器机制,通过自定义拦截器实现登录校验,并介绍了拦截器的执行流程、路径配置及适配器模式的应用,感兴趣的朋友一起看看吧
    2026-05-05
  • RocketMQ实现随缘分BUG小功能示例详解

    RocketMQ实现随缘分BUG小功能示例详解

    这篇文章主要为大家介绍了RocketMQ实现随缘分BUG小功能示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • java冷知识:javac AbstractProcessor详解

    java冷知识:javac AbstractProcessor详解

    这篇文章主要介绍了java冷知识:javac AbstractProcessor详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • SpringBoot集成缓存功能详解

    SpringBoot集成缓存功能详解

    Java Caching定义了五个核心接口,分别是:CachingProvider、CacheManager、Cache、Entry和Expiry,这篇文章主要介绍了SpringBoot集成缓存功能详细过程,需要的朋友可以参考下
    2024-06-06
  • Maven中的dependencyManagement 实例详解

    Maven中的dependencyManagement 实例详解

    dependencyManagement的中文意思就是依赖关系管理,它就是为了能通更好统一管理项目的版本号和各种jar版本号,可以更加方便升级,解决包冲突问题,这篇文章主要介绍了Maven中的dependencyManagement 实例详解,需要的朋友可以参考下
    2024-02-02

最新评论