图解 Spring 循环依赖,一文吃透!

简介: Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。

关注△mikechen的互联网架构△,10年+BAT架构经验倾囊相授


image.png

大家好,我是 mikechen | 陈睿

Spring 循环依赖如何解决?是大厂面试高频。

本篇,我们重点详解 Spring 循环依赖解决。@mikechen

循环依赖

在探讨Spring循环依赖的解决方式以前,我们先来回忆一下什么是循环依赖。

循环依赖:就是多个bean之间相互依赖,形成了一个闭环。

比如:A依赖于B、B依赖于A。

如下图所示:

image.png

体现到代码中为:

@Component
public class A{
   // 依赖B
   @Autowired
   private B b;
   public B getB() {
      return b;
   }
}

@Component
public class B {
   // 依赖A
   @Autowired
   private A a;
   public A getA() {
      return a;
   }
}

Spring的循环依赖过程:

  • 首先实例化A -> 属性填充注入B -> B还没有实例化;
  • 需要先进行实例化B(A等待) -> 实例化B -> 注入A -> A实例化未完成,无法注入 -> 实例化B失败 -> 实例化A失败。

如此反复,就进入死循环了。

Spring如何解决循环依赖

下面,我还是用 A -> B -> A 的场景,我们按照过程一步步来分析,看一下Spring 是如何解决循环依赖的。

第一步:首先是实例化A

image.png

第二步:属性注入B

执行到属性填充环节需要注入B,因为Spring管理的bean默认是单例的,为防止重复创建Spring会先去容器中查找B,如果查找不到再进行创建。

如果Spring容器中是没有B,需要先实例化B,流程和实例化一致,如下图所示:

image.png

第三步:属性注入A

此时B也执行到属性填充的环节了,此时又需要注入A,此时还是会先去Spring容器中查找A,此时的A虽然没在单例池中,但是因为在创建中并且也在三级缓存中了。

所以此时获取A的流程就发生了变化,不再是直接创建,而是会从三级缓存中获取A,如下图所示:

image.png

三级缓存存放的并不是bean对象,而是生成bean的ObjectFactory,然后放入二级缓存中,同时返回A进行依赖注入。

第四步:初始化B

此时,继续执行B的实例化, 并将B从正在创建列表移出 , 将B放入一级缓存,同时将B在二级缓存和三级缓存中删,最后返回B。

image.png

在B实例化完成并返回后,A的实例化流程也从等待着苏醒继续执行,后续流程和B的完全一致。

image.png

然后整个流程:A -> B -> A的场景就结束了。

这样Spring通过三级缓存来解决循环依赖的,提前暴露的对象存放在三级缓存中,二级缓存存放过渡bean,一级缓存存放最终形态的bean。

Spring三级缓存

// 从上至下 分表代表这“三级缓存”
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存

1.三级缓存(singletonFactories)

singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖。

2.二级缓存(earlySingletonObjects)

主要存放过渡bean,也就是三级缓存中ObjectFactory产生的对象。

提前曝光的单例对象的cache,存放原始的 bean 对象:尚未填充属性,用于解决循环依赖。

3.一级缓存(singletonObjects)

也被称为单例池,去存放已经创建完成,并且属性也注入完毕的对象,一般情况我们获取bean都是从这里获取的。

以上,是 Spring 循环依赖的详细解析,欢迎评论区留言交流或拓展。

我是 mikechen | 陈睿 ,关注【mikechen的互联网架构】,10年+BAT架构技术倾囊相授。

新的架构专题内容,第一时间更新至:阿里架构师进阶全部合集

本文已同步我的技术博客 www.mikechen.cc,更新至我原创的《30W+字阿里架构技术合集》中。

相关文章
|
6天前
|
监控 开发者
鸿蒙5.0版开发:使用HiLog打印日志(ArkTS)
在HarmonyOS 5.0中,HiLog是系统提供的日志系统,支持DEBUG、INFO、WARN、ERROR、FATAL五种日志级别。本文介绍如何在ArkTS中使用HiLog打印日志,并提供示例代码。通过合理使用HiLog,开发者可以更好地调试和监控应用。
38 16
|
7天前
|
缓存 前端开发 安全
数据同步原理
数据同步原理
35 10
数据同步原理
|
12天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
9小时前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
13 5
|
29天前
|
JavaScript 前端开发 安全
TypeScript【基础类型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第9天】TypeScript【基础类型】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
5天前
|
消息中间件 中间件 Kafka
分布式事务最全详解 ,看这篇就够了!
本文详解分布式事务的一致性及实战解决方案,包括CAP理论、BASE理论及2PC、TCC、消息队列等常见方案,助你深入理解分布式系统的核心技术。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
分布式事务最全详解 ,看这篇就够了!
|
3天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
3天前
|
SQL 缓存 监控
大厂面试高频:4 大性能优化策略(数据库、SQL、JVM等)
本文详细解析了数据库、缓存、异步处理和Web性能优化四大策略,系统性能优化必知必备,大厂面试高频。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:4 大性能优化策略(数据库、SQL、JVM等)
|
3天前
|
消息中间件 存储 Kafka
RocketMQ 工作原理图解,看这篇就够了!
本文详细解析了 RocketMQ 的核心架构、消息领域模型、关键特性和应用场景,帮助深入理解消息中间件的工作原理。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
RocketMQ 工作原理图解,看这篇就够了!
|
9小时前
|
存储 运维 安全
Snowflake 与传统数据仓库相比有哪些优势?
Snowflake 与传统数据仓库相比有哪些优势?
7 2