Spring 之循环依赖

news2024/11/15 9:47:10

        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 框架解决循环依赖的过程如下

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/410966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LT8711/LT8712 Type-C转HDMI设计方案|替代LT8711/LT8712芯片|GSV2201可完全替代兼容LT8711/LT8712

LT8711/LT8712 Type-C转HDMI设计方案|替代LT8711/LT8712芯片|GSV2201可完全替代兼容LT8711/LT8712 龙迅&#xff08;Lontium&#xff09;的LT8711/LT8712&#xff0c;是一款Type-C转HDMI 4K的视频转换芯片 通过USB Type-C连接器将DP RX视频信号转换为HDMI/DVI TX视频信号。DP…

【jvm系列-03】精通运行时数据区私有区域---虚拟机栈、程序计数器、本地方法栈

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

前端自动化测试之葵花宝典

首先聊一下概念&#xff0c;Web 前端自动化测试是一种通过编写代码来自动化执行 Web 应用程序的测试任务的方法&#xff0c;它通常使用 JavaScript 和测试框架 (如 Selenium、Appium 等) 来实现。 Web 前端自动化测试的优点是可以提高测试效率、减少测试时间和测试成本&#x…

DMA (Direct Memory Access)

DMA&#xff08;Direct Memory Access&#xff09;&#xff1a;直接存储器访问&#xff1b; 一、DMA传输将数据从一个地址空间复制到另一个地址空间&#xff0c;提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。 DMA用来提供在外设和存储器之间或者存储器和存储…

初始SpringBoot

初始SpringBoot1. SpringBoot创建和运行1.1. SpringBoot的概念1.2. SpringBoot的优点1.3. SpringBoot的创建1.3.0. 前置工作:安装插件(这是社区版需要做的工作, 专业版可以忽略)1.3.1. 社区版创建方式1.3.2. 专业版创建方式1.3.3. 网页版创建方式1.4. 项目目录介绍1.5. SpringB…

Matlab在线IDE:MATLAB Online介绍与计算定积分案例

目录1、MATLAB Online介绍功能与特点命令行窗口和编辑器窗口适用场景计费方式使用方法2、注册登录3、计算定积分1、MATLAB Online介绍 MATLAB Online是一款在线IDE&#xff08;集成开发环境&#xff09;&#xff0c;允许用户在Web浏览器中运行MATLAB并使用MATLAB工具箱&#x…

网络安全:内网渗透实例,小白也能看懂内网渗透

一、前言 从 linux 打进去到域环境&#xff0c;到获取域控权限。全篇实操 二、外网打点 1、打开站点&#xff0c;很正常的一个登录界面 2、尝试登录后发现典型的 shiro 特征。 3、使用工具直接打 shiro 反序列化即可。 4、直接上冰蝎马&#xff0c;连接 【一>所有资源获取…

【三十天精通 Vue 3】 第四天 Vue 3的模板语法详解

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录引言一、Vue 3 模板语法概述1. Vue 3 模板语法的简介2. Vue 3 模板…

如虎添翼,强大插件让ChatGPT更加游刃有余

ChatGPT模型是当前人工智能领域中备受瞩目的存在。作为一款强大的自然语言处理模型&#xff0c;它具备跨时代的意义&#xff0c;将深刻影响我们的未来。而强大的插件不仅可以丰富ChatGPT的功能&#xff0c;提高其应对复杂问题的能力。还也可以解决一些常见的错误&#xff0c;如…

springboot打包成jar和war浅析

问题1&#xff1a;一个springboot项目&#xff0c;用mvn install打包成jar&#xff0c;换一台有jdk的机器就直接可以用java -jar 项目名.jar的方式运行&#xff0c;没任何问题&#xff0c;为什么这里不需要tomcat也可以运行了&#xff1f; 问题2&#xff1a;一个springboot项目…

js非常的混乱怎么学才能入门呢?

前言 ES5还是要学的喔&#xff0c;里面有很多重要的概念&#xff0c;跟ES6有着很强的关联性&#xff0c;大致上包括&#xff1a; 变量声明 ES5 使用var关键字来声明变量&#xff0c;而 ES6 引入了 let 和 const 关键字&#xff0c;用于声明块级作用域的变量和常量。这些新的关…

[图神经网络]空间关系感知关系网络(SGRN)-代码解析

&#xff01;&#xff01;&#xff01;这篇不涉及实现&#xff0c;仅从官方代码了解一下输出处理的思路&#xff0c;有机会的话会做实现&#xff0c;照例放出官方代码地址和之前写的论文解读&#xff1a; SGRN网络github项目地址https://github.com/simblah/SGRN_torch[图神经…

利用三个云服务器,搭建MongoDB副本集模式(主从模式)

1. 下载安装mongoDB 首先我们需要在三台服务器上分别下载和安装mongoDB。 1.1. 打开服务器&#xff0c;创建目录 创建目录结构如下图所示&#xff1a;&#xff08;日志文件会自动创建&#xff09; 1.2. 下载mongoDB压缩包 把压缩包下载到指定目录&#xff08;便于后期维护…

ChatGPT大规模封号+停止注册?最火概念会凉吗?

一、背景 这个周末&#xff0c;先是意大利暂时封杀ChatGPT&#xff0c;限制OpenAI处理本国用户信息。 接着&#xff0c;据韩国媒体报道&#xff0c;三星导入ChatGPT不到20天&#xff0c;便曝出机密资料外泄&#xff08;涉及半导体设备测量资料、产品良率等内容&#xff0c;已…

微信小程序 | 秋招颗粒无收 ?快用ChatGPT做一款模拟面试小程序

Pre&#xff1a;效果预览 ① 选择职位进行面试 ② 根据岗位职责进行回答 一、需求背景 这两年IT互联网行业进入寒冬期&#xff0c;降本增效、互联网毕业、暂停校招岗位的招聘&#xff0c;各类裁员、缩招的情况层出不穷&#xff01;对于这个市场来说&#xff0c;在经历了互联网…

阿里云的客服 锻炼你心性的 一种方式 !!!

阿里云的产品&#xff0c;非常棒&#xff0c;开发的同学非常棒&#xff0c;专家们更棒&#xff0c;但&#xff0c;一切的开始就怕一个但字&#xff0c;但我还的说&#xff0c;但&#xff0c;阿里云的客服&#xff0c;OMG &#xff0c;我已经忍耐了 1年了&#xff0c;是在忍不住…

手麻系统源码,手术麻醉管理系统源码,维护方便,功能强大

手术麻醉管理系统源码&#xff0c;手麻系统源码&#xff0c;C# .net 桌面软件 C/S版 文末获取联系&#xff01; 手术麻醉管理系统采用下拉式汉化菜单&#xff0c;界面友好&#xff0c;实用性强&#xff0c;设有与住院、病区、药房等系统的软件接口。 开发语言&#xff1a;C# …

代码随想录算法训练营第五十三天 | 1143. 最长公共子序列、1035. 不相交的线、53. 最大子数组和

1143. 最长公共子序列 动规五部曲 1、确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j]&#xff1a;长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j] 2、确定递推公式 主要就是两大情况&#xff1a; text1[i…

vue+ts+vite+pinia+element plus+i18n国际化

第一步&#xff0c;安装vue-i18n&#xff08;我这里版本是9.2.2&#xff09; npm install vue-i18n element-plus --save 第二步&#xff0c;src文件夹下创建language文件夹&#xff0c;目录如下 第三步&#xff0c;定义本地中文英文 en.ts // en.ts export default {message…

UE DTCmd 插件说明

Exec CMD Exec CMD (Have Process) 在蓝图非阻塞的运行Windows命令行并输出返回值&#xff0c;而且可以时时监听输出内容。 可以直接运行某个程序&#xff08;输入程序完整路径&#xff09; 可以直接运行bat脚本&#xff0c;并在bat脚本里面运行你任何想做的操作。 Cmd : 需要…