首页 > 其他分享 >深入理解 Spring 三级缓存:解决单例 Bean 循环依赖的利器

深入理解 Spring 三级缓存:解决单例 Bean 循环依赖的利器

时间:2024-08-17 21:56:27浏览次数:7  
标签:缓存 Spring 循环 Bean 依赖 单例 三级

目录

一、什么是循环依赖?

二、关于传说中的三级缓存

1.基本概念:

2.三级缓存是哪三级? 

3.【举个例子】那三级缓存是怎么解决上述代码例子中的A、B互相依赖呢?

详细过程:(理解用)

简约版:(理解后看这个就行)

三、关于三级缓存的适用范围

适用的循环依赖范围:

不适用的循环依赖范围:

四、那只用二级缓存行不行?

1.只用二级缓存:

2.三级缓存的优点:


一、什么是循环依赖?

     循环依赖发生在两个或多个 Bean 之间相互依赖的情况下。

     例如,假设我们有两个 Bean:AB,其中 A 依赖 B,而 B 又依赖 A,这就形成了循环依赖。如下所示:

//在这种情况下,Spring 在创建 Bean A 时需要注入 B,
//而创建 B 时又需要注入 A,这就导致了一个无法打破的循环。
@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

有一点“死锁”的味道。


二、关于传说中的三级缓存

1.基本概念:

      三级缓存通过在 Bean 的创建过程中提前暴露未完全初始化的 Bean 的引用,来解决单例 Bean 的循环依赖问题。

      大白话:“管你初始化没初始化好,先拉出来遛遛~”,就是把还没准备好的对象,先拉出来用用,在用的过程中该对象也会逐步配置完全。


2.三级缓存是哪三级? 

· 三级缓存singletonFactories:

      存储的是用于创建单例 Bean 实例的工厂对象。

      在 Bean 创建的早期阶段,Spring 会将一个工厂对象放入三级缓存,以便在需要时生成 Bean 的早期引用。这是为了在需要解决循环依赖时,可以通过这个工厂对象获取到 Bean 的引用。创建的 Bean 引用随后可以被放入二级缓存。

·  二级缓存 earlySingletonObjects:

      用于存储早期暴露的单例 Bean。这些 Bean 已经实例化但还未完成依赖注入和初始化过程

       当一个 Bean 在创建过程中需要提前暴露自身(通常是为了解决循环依赖),Spring 会将其放入二级缓存中,以便其他 Bean 可以引用这个未完全初始化的 Bean。

·  一级缓存:singletonObjects

     这是最主要的缓存,用于存储完全初始化好的单例 Bean 对象。

     当一个 Bean 完全初始化完成后,Spring 会将它放入这个一级缓存中。在后续获取该 Bean 时,Spring 直接从这里获取已经初始化好的实例。


3.【举个例子】那三级缓存是怎么解决上述代码例子中的A、B互相依赖呢?

详细过程:(理解用)

a. 创建 A 的实例

- 当 Spring 容器开始创建 A 时,它会先调用 A 的构造函数来实例化 A 对象。

- 这个时候,A 还没有完成依赖注入(即 B 还没有注入到 A 中),只是创建了 A 的一个原始实例

 b. 将 A 的早期引用放入三级缓存

- Spring 识别到可能存在循环依赖问题,因此它会将 A 的一个工厂对象(ObjectFactory<A>)放入三级缓存中 (singletonFactories)。

- 这个工厂对象用于在后续依赖注入过程中,提供 A 的早期引用(即尚未完全初始化的 A 实例)。

c. 创建 B 的实例

- 紧接着,Spring 需要创建 B,于是调用 B 的构造函数来实例化 B 对象。

- 在 B 的实例化过程中,Spring 发现 B 依赖于 A(因为 B 中有 @Autowired 注解的 A)

d. 从三级缓存中获取 A 的早期引用

- Spring 检查三级缓存,发现 A 的工厂对象已存在。它从三级缓存中获取 A 的早期引用,并将这个引用放入二级缓存 (earlySingletonObjects) 中,同时从三级缓存中移除该工厂对象。

- 此时,B 就可以引用 A 的这个早期引用,完成 B 的依赖注入。

e. 完成 B 的创建

- B 完成依赖注入和初始化后,Spring 将 B 放入一级缓存 (singletonObjects) 中,表示 B 已经完全创建并准备好被使用。

 f. 回到 A,完成依赖注

- 现在,B 已经创建完毕,回到 A,继续完成 A 的依赖注入过程。这时 B 已经存在于一级缓存中,可以直接注入到 A 中。

- 最后,A 也完成了初始化,并被放入一级缓存。

简约版:(理解后看这个就行)

1. 创建 A 实例 → A 放入三级缓存 → 开始创建 B

2. 创建 B 实例 → B 发现依赖 A → 从三级缓存获取 A 的早期引用 → A 的早期引用放入二级缓存

3. B 完成创建 → B 放入一级缓存

4. 回到 A → 从一级缓存获取 B → 完成 A 的创建 → A 放入一级缓存


三、关于三级缓存的适用范围

适用的循环依赖范围:

Spring 三级缓存机制主要用于解决单例(Singleton)作用域的 Bean 循环依赖问题。


     细心的读者已经发现了,标题强调的是“单例Bean循环依赖”,且上述解决AB相互依赖的详细步骤中,关于A、B实例的创建的标红部分


不适用的循环依赖范围:

1.原型(Prototype)作用域的循环依赖

      对于原型作用域的 Bean,每次请求都会创建一个新的实例。因此,原型 Bean 没有全局缓存,也不会在创建过程中被缓存起来没有缓存机制,Spring 无法像处理单例 Bean 那样通过三级缓存提前暴露 Bean 的引用来解决循环依赖。

    如果原型 Bean 之间存在循环依赖,Spring 无法使用三级缓存解决这个问题,通常会抛出 BeanCurrentlyInCreationException

2.构造函数注入的循环依赖

     当两个 Bean 通过构造函数相互依赖时,Spring 无法通过三级缓存来解决这种类型的循环依赖,因为即使是生成原始对象,也要通过构造函数

3.AOP 代理引发的复杂循环依赖

     在使用 AOP时,Spring 可能会创建代理对象。如果 AOP 代理涉及的 Bean 存在循环依赖,AOP 代理对象和原始 Bean 之间的关系可能导致复杂的依赖链,这种依赖链可能无法通过三级缓存的简单缓存机制来解决

4.某些非单例作用域的自定义 Bean Scope

     除了单例和原型作用域外,Spring 允许定义自定义的作用域。如果这种自定义作用域的生命周期和依赖处理方式与单例不同,三级缓存机制可能无法适用于这些自定义作用域的 Bean。

5.循环依赖与非自动装配的复杂场景

      某些情况下,Bean 的依赖关系并非通过 Spring 自动装配管理,而是通过手动获取或其他复杂的方式管理。这种情况下,三级缓存机制可能无法适用。


四、那只用二级缓存行不行?

1.只用二级缓存:

      细心的小伙伴就会问了,那上面的第三级缓存是不是可以砍掉,⼀级缓存保存完整的Bean,⼆级缓存保存不完整的Bean。是这样的道理,二级缓存也能满足循环依赖的注入。在Spring3.6之前,Spring是通过使⽤⼀级和⼆级缓存来解决单例Bean的循环依赖问题。

二级局限性:过早暴露Bean

    仅使用二级缓存,在创建一个 Bean 时,需要立即将其尚未完全初始化的实例放入二级缓存中。这可能会导致半成品 Bean 被过早地使用,容易引发问题,尤其是在 Bean 的初始化步骤中可能进行一些关键操作(如依赖检查、代理生成等)。


2.三级缓存的优点:

      从 3.6版本开始,通过引入三级缓存,Spring能够更全⾯地处理包括AOP在内的更复杂的循环依赖情况。 三级缓存中的 存储的是工厂对象  而不是 Bean 的实例,这样在需要时可以动态生成 Bean 的引用。

       这种设计允许 Spring 在真正需要的时候才将早期引用暴露出来,而不是在 Bean 实例创建的最初阶段。这种延迟初始化方式可以避免很多潜在的并发问题和初始化过程中不一致的情况。

标签:缓存,Spring,循环,Bean,依赖,单例,三级
From: https://blog.csdn.net/weixin_71246590/article/details/141200698

相关文章

  • SpringMVC - 进阶
    1.Controller&RequestMapping@Controller用来标注在类上,表示这个类是一个控制器类,可以用来处理http请求,通常会和@RequestMapping一起使用。这个注解上面有@Component注解,说明被@Controller标注的类会被注册到spring容器中,value属性用来指定这个bean的名称@Target({Element......
  • 二分查找不理解?一篇弄懂!--基础二分查找算法详细解释(带简单例题的详细解法)
    本文参考:灵茶山艾府分享丨【题单】二分算法(二分答案/最小化最大值/最大化最小值/第K小)-力扣(LeetCode)二分查找红蓝染色法_哔哩哔哩_bilibili本文主要详细讲解基础的二分算法中的查找,包括原理和模板,并用leetcode和洛谷的一些例题来进行实际题目讲解,如果觉得有帮助或者写......
  • springboot自动配置原理
    springboot自动配置原理springboot的出现就是为了简化Spring框架的开发,快速构建java项目。springboot的两个核心特性就是起步依赖和自动配置。起步依赖原理:maven的依赖传递自动配置原理:条件注入、SPI机制起步依赖假如我们没有使用SpringBoot,用的是Spring框架进行web程序的开......
  • application.yml文件配置springboot项目
    基本用法#注意空格都不能省#配置端口号server:port:8080address:127.0.0.1#配置数据库spring:datasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://localhost:3306/tliasusername:rootpassword:root#定义对象/Ma......
  • 【计算机毕设项目推荐】基于SpringBoot的springboot单位考勤系统的管理设计与实现
    前言:我是天码编程,从事计算机开发行业数年,专注Java程序设计开发、源码分享、技术指导和毕业设计,欢迎各位前来交流讨论......
  • 【计算机毕设选题推荐】基于SpringBoot的springboot博物馆展览与服务一体化平台的设计
    前言:我是天码编程,从事计算机开发行业数年,专注Java程序设计开发、源码分享、技术指导和毕业设计,欢迎各位前来交流讨论......
  • 【Java毕设选题推荐】基于SpringBoot的springboot基于Android的房屋租赁App
    前言:我是IT源码社,从事计算机开发行业数年,专注Java领域,专业提供程序设计开发、源码分享、技术指导讲解、定制和毕业设计服务......
  • Spring设计理念
    AOP基础概念AOP,也就是Aspect-orientedProgramming,译为面向切面编程,我们可以简单的把AOP理解为贯穿于方法之中,就好比我们今天的主题——日志功能,就是一个典型的案例。简单使用1)横切关注点,从每个方法中抽取出来的同一类非核心业务。2)切面(Aspect),对横切关注点进行封装的类,每......
  • springboot+vue宠物领养系统【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景在当今社会,随着人们生活水平的提高和情感需求的日益增长,宠物已成为许多家庭不可或缺的一员。然而,宠物领养过程中存在信息不对称、领养渠道不畅、宠物健康信息不透明等问题,给宠物爱好者带来了诸多困扰。同时,流浪动物数量的不断增加也引......
  • springboot+vue宠物领养系统【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景随着现代生活节奏的加快和人们情感需求的日益增长,宠物已成为许多家庭不可或缺的一员,它们不仅提供了陪伴与慰藉,还促进了人与自然的和谐共处。然而,宠物数量的激增也带来了诸如流浪动物增多、宠物领养信息不对称等问题。传统的宠物领养方......