首页 > 编程语言 >高级java每日一道面试题-2025年01月13日-框架篇[Spring篇]-Spring 是怎么解决循环依赖的?

高级java每日一道面试题-2025年01月13日-框架篇[Spring篇]-Spring 是怎么解决循环依赖的?

时间:2025-01-13 20:58:04浏览次数:3  
标签:初始化 面试题 01 依赖 Spring Bean 循环 注入

如果有遗漏,评论区告诉我进行补充

面试官: Spring 是怎么解决循环依赖的?

我回答:

在Java高级面试中,Spring框架如何解决循环依赖是一个重要且常见的问题。以下是对Spring解决循环依赖的详细解释:

循环依赖的定义与类型

循环依赖是指两个或多个Bean之间互相依赖,形成一个闭环。在Spring框架中,循环依赖通常发生在依赖注入(Dependency Injection)过程中。循环依赖的类型主要包括构造函数循环依赖和属性(或Setter)循环依赖。

Spring 如何检测和解决循环依赖?

Spring 使用了多种策略来检测并解决循环依赖问题,主要包括以下几种方式:

单例 Bean 的三级缓存机制

Spring 容器为每个单例 Bean 维护了三个不同级别的缓存:

  • singletonObjects:存放已经完全初始化完成的 Bean 实例。
  • earlySingletonObjects:存放尚未完成初始化但已经被实例化的 Bean(即半成品 Bean)。这些 Bean 已经完成了构造函数注入,但是还没有完成属性注入和其他初始化方法调用。
  • singletonFactories:存放 FactoryBean 或者用于创建早期暴露对象的工厂方法。

当 Spring 检测到 A 和 B 之间的循环依赖时,它会按照如下步骤操作:

  1. 创建 Bean A:Spring 开始创建 Bean A,并将其放入 singletonFactories 中。
  2. 实例化 Bean A:接着实例化 Bean A 并设置其非循环依赖的属性。
  3. 提前暴露 Bean A:将 Bean A 提前暴露给其他 Bean 使用,此时 Bean A 被移入 earlySingletonObjects 缓存。
  4. 创建 Bean B:尝试创建 Bean B,在此过程中发现它依赖于 Bean A。
  5. 获取 Bean A:由于 Bean A 已经存在于 earlySingletonObjects 中,所以可以直接获取并设置到 Bean B 上。
  6. 继续初始化 Bean A:回到 Bean A 的初始化流程,设置它的剩余属性(包括对 Bean B 的引用),最后将 Bean A 移入 singletonObjects 缓存。
  7. 完成 Bean B 的初始化:现在可以安全地完成 Bean B 的初始化,因为它已经获得了对 Bean A 的引用。
    通过这种方式,Spring 成功地打破了循环依赖,使得两个相互依赖的 Bean 都能正常初始化。
构造器注入 vs 字段/Setter 注入
  • 构造器注入:如果使用构造器注入,则在构造函数中传递所有必需的依赖项。在这种情况下,如果存在循环依赖,Spring 将抛出异常,因为无法同时满足两个 Bean 的构造需求。
  • 字段/Setter 注入:相比之下,字段注入或 Setter 方法注入允许 Spring 在实例化之后再设置依赖关系,这为解决循环依赖提供了可能。因此,推荐在需要支持循环依赖的情况下优先考虑字段或 Setter 注入。
@Lazy 注解

对于某些特定场景下的循环依赖问题,可以使用 @Lazy 注解延迟加载某个 Bean,直到真正需要它的时候才进行实例化。这样可以避免在启动阶段就触发循环依赖错误。

@Component
public class ServiceA {
    private final ServiceB serviceB;

    @Autowired
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
代理模式

Spring 还可以通过 CGLIB 动态代理的方式生成目标 Bean 的代理类,然后在代理类中实现对原始 Bean 的懒加载。这种方式适用于接口类型的 Bean,能够有效缓解循环依赖问题。

Spring无法解决的循环依赖情况

需要注意的是,Spring的循环依赖解决机制有一些限制:

  1. 原型作用域的Bean:对于原型作用域的Bean,由于每次请求都会创建一个新的Bean实例,因此无法使用缓存来解决循环依赖。
  2. 构造器注入的循环依赖:如果Bean的构造方法中存在循环依赖,Spring也无法解决。因为在构造方法中,Bean实例还未创建,无法放入缓存。

解决循环依赖的最佳实践

尽管Spring提供了解决循环依赖的机制,但在设计时仍应尽量避免出现循环依赖,因为循环依赖可能导致代码的可读性差,并且可能是设计上的问题。以下是一些解决循环依赖的最佳实践:

  1. 模块化:将代码拆分成独立的模块,使每个模块只负责一个功能,降低模块间的耦合度。
  2. 使用依赖注入:通过依赖注入,将依赖关系从代码中解耦,使得一个类不再直接依赖另一个类,而是依赖于一个接口或抽象类。
  3. 使用设计模式:利用设计模式(如观察者模式、中介者模式等)来帮助更好地组织代码,避免循环依赖的产生。
  4. 代码重构:定期对代码进行重构,消除潜在的循环依赖问题。

标签:初始化,面试题,01,依赖,Spring,Bean,循环,注入
From: https://blog.csdn.net/qq_43071699/article/details/145123534

相关文章

  • 2025毕设springboot 服装搭配推荐系统论文+源码
    系统程序文件列表开题报告内容研究背景随着时尚产业的蓬勃发展,个性化与多样化的服装需求日益增长,消费者在面对琳琅满目的商品时,往往难以快速找到符合自身风格与需求的搭配方案。传统的购物模式受限于时间、空间以及个人审美经验的局限,难以满足现代消费者对于高效、精准搭配......
  • 2025毕设springboot 扶贫助农与产品合作系统论文+源码
    系统程序文件列表开题报告内容研究背景在当今社会,扶贫助农已成为国家发展的重要战略之一。随着信息技术的飞速发展和互联网的普及,如何利用现代科技手段促进农村地区经济发展,提高农民收入,成为亟待解决的问题。传统的扶贫方式往往局限于物质援助,缺乏持续性和内生动力。因此,构......
  • 2025毕设springboot 峰数公司医疗设备管理系统论文+源码
    系统程序文件列表开题报告内容研究背景随着医疗技术的不断进步和医疗需求的日益增长,医疗设备在现代医疗体系中扮演着至关重要的角色。峰数公司作为一家专注于医疗设备供应与服务的公司,面临着设备种类繁多、管理复杂、信息碎片化等挑战。传统的设备管理手段已难以满足当前高......
  • Java-数据结构-栈与队列(常考面试题与单调栈)
    在上一篇的学习中,我们学习了栈和队列的基本知识,以及它们对应都有哪些方法,在什么应用场景下如何使用,并且还对它们进行了模拟实现,而其实对于栈和队列的相关知识还远不止于此,而今天我们就对栈与队列进行复盘,认识更多使用它们的场景,夯实代码功底吧~一、常考面试题-思维以下习题在......
  • 2025毕设springboot 飞扬城市交通系管理系统设计与实现论文+源码
    系统程序文件列表开题报告内容研究背景随着城市化进程的加速,飞扬城市交通拥堵、交通事故频发等问题日益凸显,给市民的日常出行和城市的经济发展带来了严峻挑战。传统的交通管理方式已难以满足当前复杂多变的交通需求,因此,构建一个高效、智能的城市交通管理系统显得尤为重要。......
  • 2025毕设springboot 飞机票管理系统论文+源码
    系统程序文件列表开题报告内容研究背景随着全球航空业的迅猛发展,飞机已经成为现代社会中不可或缺的交通工具之一。伴随着航班数量的激增,机票管理成为航空公司运营中的关键环节。传统的机票管理方式往往依赖于人工操作,不仅效率低下,还容易出现信息错漏等问题。特别是在高峰期......
  • 2025毕设springboot 非全日制研究生管理系统设计与实现论文+源码
    系统程序文件列表开题报告内容研究背景随着教育改革的深入和终身学习理念的普及,非全日制研究生教育逐渐成为我国高等教育体系中的重要组成部分。非全日制研究生群体因其学习方式的特殊性,如学习时间分散、学习地点灵活等,给高校的教学管理带来了诸多挑战。传统的管理方式已难......
  • springboot嘉旺白酒业销售管理系统分析与设计
    目录系统实现截图开发核心技术介绍技术栈核心代码部分展示操作手册视频演示/源码获取系统实现截图开发核心技术介绍springboot+Element-UI+vue本系统采用MVVM模式,开发框架使用SpringBoot框架,开发工具使用IDEA,VisualStudioCode,Web服务器使用Tomcat,数据库......
  • 【事件分析】20250112-Usual 赎回机制调整事件
    背景信息https://docs.usual.money/Usual是一个聚合RWA的稳定币发行协议,经济模型中存在三种代币:USD0:Usual发行的稳定币。USD0++:USD0++是USD0的质押版本,为期4年,可获得USUAL代币奖励。USUAL:Usual协议的治理代币。事发缘由https://usual.money/blog/usual-s-next-......
  • 如何提高 SpringBoot 的代码质量?
    保持好的代码质量和遵守编码标准是开发可维护和健壮软件的重要方面。在SpringBoot应用程序中,确保始终遵循命名约定、代码结构和其他质量标准是一项艰巨的任务,尤其是当项目的复杂性和规模不断扩大时更是如此。“在本文中,我们将探讨如何使用Java反射来提高SpringBoot......