最通俗的方式理解Spring循环依赖三级缓存

简介: 有位粉丝找我,说要耽误我5分钟时间,想让我帮助它理解一下Spring循环依赖的三级缓存,绕晕了一个星期,没有想明白。我想今天,用最通俗易懂的方式给大家重新梳理一下,保证让你听懂了。

有位粉丝找我,说要耽误我5分钟时间,想让我帮助它理解一下Spring循环依赖的三级缓存,绕晕了一个星期,没有想明白。我想今天,用最通俗易懂的方式给大家重新梳理一下,保证让你听懂了。

1、什么是循环依赖?

    循环依赖就是指循环引用,是两个或多个Bean相互之间的持有对方的引用。循环依赖有三种形态:

1、相互依赖,也就是A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。

f04e6d39c894a34c2938f87b9e0f7a95.png

2、三者间依赖,也就是A 依赖 B,B 依赖 C,C 又依赖 A,形成了循环依赖。

13ec8528e7f2c707e1469efcb78fc7f3.png

3、自我依赖,也是A依赖A形成了循环依赖自己依赖自己。

e6f0d46abba592afca5b15e7ecf56dbb.png

2、如何解决循环依赖问题?

循环依赖本身没有问题,问题是Spring中加入了依赖注入机制,也就是自动给属性赋值。当创建Bean实例化以后,需要给Bean中需要赋值的属性全部自动赋值才能交给用户使用。但如果是循环依赖的情况,以两个Bean相互依赖的情况为例,

2a305db44bc646f3e3dd0a6194faa5d4.png

假设Bean A已经实例化,但是Bean A中需要自动赋值Bean B并没有初始化,但如果Spring立刻去初始化Bean B,发现Bean B中需要自动赋值的Bean A没有初始化,如果这样相互等待,就会形成死循环,最终,有可能导致Spring容器都无法启动。

7f9846277794c182c9e337e09bb76820.png

就好比,我们以前读书的时候,老师经常教我们一个考试方法,就是遇到难题不会答的时候,不要死磕,要继续往下做其他的题。否则,会因为一道难题卡住影响到整个的答题进度,还会影响正常的发挥,影响考试结果。

那这个问题该怎么解决呢?使用缓存。

11daa34d407c8f2e289aa5407e30ccf4.png

就是将所有实例化好的Bean,全部放到一个容器中缓存起来,并且将已经完成实例化但没有完成赋值的,打上标记。

b74adb4e959dad1271e84fef0b9f5bae.png

然后,等Bean全部实例化以后,再重新扫描一遍容器,将没有完成赋值的Bean属性完成赋值,这个时候,所有未完成赋值的Bean都已经能够找到对应的实例了。


那么问题来了。解决循环依赖问题,一定要二级缓存吗?答案是不一定。但是Spring中为什么又要设计二级缓存呢?

f9627c5b07fc532bb328fbdecc673c24.png

这时候,我们可以这样理解,假设,我们只有一个缓存容器,并且缓存是直接开放给用户可以调用的,如果将未完成赋值的Bean和已完成赋值的Bean全部放到同一个容器,那这个时候,调用者就有可能拿到未赋值的Bean,这样的Bean对于用户来说是不可用的,可能会导致空指针异常。

ea320b10279e9759383fba161c1137be.png

所以,Spring设计者,才有了这样一个设计,将能够直接提供给用户使用的Bean放到一级缓存中,这样Bean称之为终态Bean,或者叫成熟Bean。

将已经完成初始化,但还不能提供给用户使用的Bean单独放到一个缓存容器中,就是二级缓存,这样的Bean称之为临时Bean,或者叫早期Bean。

依照以上的分析,理论上二级缓存就能解决循环依赖问题,那为什么Spring还要设计一个三级缓存呢?

3、如何理解三级缓存?

434fb3f5cfc6dadc6ed4ede44c59b69a.png

我们都知道,Spring中有很多注入的Bean是需要创建代理Bean的,但是,不是所有的Bean都需要再实例化之后立马就会创建代理Bean。是要等到Bean初始化全部完成之后才创建代理Bean。因此,循环依赖的出现,Spring又不得不去提前创建代理Bean。如果不创建代理Bean,注入原始Bean就会产生错误。因  此,Spring设计三级缓存,专门用来存放代理Bean。但是,创建代理Bean的又不同的规则,因此,Spring三级缓存中,并不是直接保存代理Bean的引用,而是保存创建代理Bean的Factory。

4、总结

所以,总结结论为,单纯解决循环依赖可以只用二级缓存,但是如果涉及到代理对象的循环依赖,就需要用到三级缓存。其实一、二、三级缓存是根据获取对象的顺序来命名的,我们完全可以这样理解,一级缓存就是终态缓存,二级缓存是临时缓存、三级缓存是代理工厂的缓存。

8be720c5ebcc0865ee01a367387ad24f.png

这张图完整地描述了一、二、三级缓存的运行逻辑。

看完今天分享之后,你还觉得循环依赖的三级缓存难吗?听懂的小伙伴,关注点个赞,下次不迷路。

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

相关文章
|
2月前
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
54 2
|
1月前
|
缓存 NoSQL Java
Spring Boot中的分布式缓存方案
Spring Boot提供了简便的方式来集成和使用分布式缓存。通过Redis和Memcached等缓存方案,可以显著提升应用的性能和扩展性。合理配置和优化缓存策略,可以有效避免常见的缓存问题,保证系统的稳定性和高效运行。
47 3
|
1月前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
59 4
|
2月前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
348 2
|
4月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
234 24
|
3月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
77 1
|
4月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
51 4
|
4月前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
56 1
|
5月前
|
缓存 Java Spring
spring如何解决循环依赖
Spring框架处理循环依赖分为构造器循环依赖与setter循环依赖两种情况。构造器循环依赖不可解决,Spring会在检测到此类依赖时抛出`BeanCurrentlyInCreationException`异常。setter循环依赖则通过缓存机制解决:利用三级缓存系统,其中一级缓存`singletonObjects`存放已完成的单例Bean;二级缓存`earlySingletonObjects`存放实例化但未完成属性注入的Bean;三级缓存`singletonFactories`存放创建这些半成品Bean的工厂。