图解 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+字阿里架构技术合集》中。

相关文章
|
16天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
3月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
227 24
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
2月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
69 1
|
3月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
46 4
|
4月前
|
存储 缓存 Java
面试问Spring循环依赖?今天通过代码调试让你记住
该文章讨论了Spring框架中循环依赖的概念,并通过代码示例帮助读者理解这一概念。
面试问Spring循环依赖?今天通过代码调试让你记住
|
4月前
|
缓存 Java Spring
spring如何解决循环依赖
Spring框架处理循环依赖分为构造器循环依赖与setter循环依赖两种情况。构造器循环依赖不可解决,Spring会在检测到此类依赖时抛出`BeanCurrentlyInCreationException`异常。setter循环依赖则通过缓存机制解决:利用三级缓存系统,其中一级缓存`singletonObjects`存放已完成的单例Bean;二级缓存`earlySingletonObjects`存放实例化但未完成属性注入的Bean;三级缓存`singletonFactories`存放创建这些半成品Bean的工厂。
|
4月前
|
Java Spring 容器
循环依赖难破解?Spring Boot神秘武器@RequiredArgsConstructor与@Lazy大显神通!
【8月更文挑战第29天】在Spring Boot应用中,循环依赖是一个常见问题。当两个或多个Bean相互依赖形成闭环时,Spring容器会陷入死循环。本文通过对比@RequiredArgsConstructor和@Lazy注解,探讨它们如何解决循环依赖问题。**@RequiredArgsConstructor**:通过Lombok生成包含final字段的构造函数,优先通过构造函数注入依赖,简化代码但可能导致构造函数复杂。**@Lazy**:延迟Bean的初始化,直到首次使用,打破创建顺序依赖,增加灵活性但可能影响性能。根据具体场景选择合适方案可有效解决循环依赖问题。
157 0
|
5月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
5月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
5月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决