锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. JAVA
  4. Spring 的三级缓存机制

Spring 的三级缓存机制

0
  • JAVA
  • 发布于 2024-08-07
  • 0 次阅读
黄健
黄健

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

Spring 的三级缓存机制

Spring 的三级缓存机制是解决循环依赖的关键。

Spring 框架为了解决循环依赖问题,设计了一套三级缓存机制。这三级缓存分别是:

  1. 一级缓存 singletonObjects:这是最常规的缓存,用于存放完全初始化好的 bean。如果某个 bean 已经在这个缓存中,则直接返回这个 bean 的实例。
  2. 二级缓存 earlySingletonObjects:这个缓存用于存放早期暴露出来的 bean,也就是那些已经创建但尚未完成初始化(如属性填充和初始化方法调用)的 bean。这样做的目的是为了在 bean 的创建过程中就能提前暴露出来,以便于解决循环依赖的问题。
  3. 三级缓存 singletonFactories:这个缓存存放的是 bean 的工厂对象,这些工厂对象负责生成 bean 的实例。当一个 bean 被创建时,它的工厂对象会被放入这个缓存中。

通过这三级缓存,Spring 能够在 bean 的创建过程中就解决循环依赖的问题。具体来说,当 AOP 代理对象需要引用其他 bean 时,可以通过提前暴露的二级缓存来获取尚未完全初始化的 bean,从而打破循环依赖的僵局。

以下是一个简单的例子来说明这个过程:

假设有两个 bean,A 和 B,它们相互依赖。当 Spring 容器开始创建 bean A 时,它会首先检查一级缓存中是否已经有了 bean A 的实例。如果没有,它会创建一个 bean A 的实例,并将其工厂对象放入三级缓存中。然后,bean A 的创建过程会因为需要注入 bean B 而被挂起,Spring 会转而去创建 bean B。

在创建 bean B 的过程中,同样会检查一级缓存中是否已经有了 bean B 的实例。由于还没有创建,Spring 会将 bean B 的半成品(即尚未完成初始化的实例)放入二级缓存中,并继续创建过程。这时,bean B 也需要依赖 bean A,但由于 bean A 的工厂对象已经在三级缓存中,Spring 可以直接从三级缓存中获取到 bean A 的工厂对象,并通过它来创建 bean A 的实例。

这样,即使两个 beans 相互依赖,Spring 也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

参考文章——Spring 中的 “三级缓存”

参考文章——Spring 面试题之循环依赖与三级缓存

为什么 Spring 只有二级缓存不行

Spring 只有二级缓存确实无法完全解决循环依赖问题,尤其是在涉及到 AOP 代理时。

二级缓存earlySingletonObjects用于存储半成品的 Bean 实例,即那些已经被实例化但尚未完成初始化(如属性填充和方法调用)的 Bean。这个缓存允许 Spring 在 Bean 的创建过程中就能提前暴露出来,以便于解决循环依赖的问题。然而,如果只有二级缓存,当涉及到 AOP 代理时,问题就来了。

AOP 代理的生成是在 Bean 的初始化阶段完成的,这意味着在 Bean 的所有属性都被设置之后。如果一个 Bean 需要被代理,那么在代理之前,Spring 会尽量从缓存中获取到原始的 Bean 实例,以避免在代理过程中出现循环引用的问题。但是,如果只有二级缓存,那么在 Bean 初始化之前,我们无法从缓存中获取到代理对象,因为二级缓存中存储的是尚未初始化的 Bean 实例,而不是代理对象。

举个例子,假设有两个 Bean,A 和 B,它们相互依赖,并且 A 需要被 AOP 代理。在 Spring 的创建过程中,首先会创建 A 的实例并将其放入二级缓存中。然后,当尝试创建 B 并注入 A 时,会发现 A 还没有完成初始化,因此无法生成 A 的代理对象。这样就会导致循环依赖的问题无法被解决。

而三级缓存中的singletonFactories存储的是 Bean 的工厂对象,可以在 Bean 初始化之前就生成代理对象,并将其放入一级缓存singletonObjects中。这样,即使 Bean 之间存在循环依赖,Spring 也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

总的来说,三级缓存机制是 Spring 为了在保持设计原则的同时,解决循环依赖和 AOP 代理的问题而设计的。二级缓存虽然可以解决部分循环依赖的问题,但在面对 AOP 代理时就显得力不从心了。因此,Spring 需要三级缓存来确保在复杂情况下依然能够正常工作。

让我们通过一个具体的例子来理解为什么仅有二级缓存无法解决涉及 AOP 代理的循环依赖问题。

假设我们有两个 Bean,ServiceA和ServiceB,它们相互依赖,并且ServiceA需要被 AOP 代理(例如,为了实现事务管理)。

  1. 创建 ServiceA 实例:Spring 首先尝试创建ServiceA的实例。在实例化过程中,ServiceA尚未完成初始化(例如,还未填充属性,未调用初始化方法等),但它的半成品实例被放入了二级缓存earlySingletonObjects。

  2. 创建 ServiceB 实例:接着,Spring 尝试创建ServiceB的实例,并试图注入ServiceA。由于ServiceA的完整初始化尚未完成,因此无法生成其 AOP 代理。

  3. AOP 代理问题:在 Spring 中,AOP 代理是在 Bean 的初始化阶段完成的,这意味着所有的属性填充和方法调用之后。如果ServiceA需要被代理,那么在代理之前,Spring 会尝试从缓存中获取到原始的ServiceA实例,以避免在代理过程中出现循环引用的问题。但是,如果只有二级缓存,那么在ServiceA初始化之前,我们无法从缓存中获取到代理对象,因为二级缓存中存储的是尚未初始化的ServiceA实例,而不是代理对象。

  4. 循环依赖:由于ServiceB需要注入ServiceA,而ServiceA又需要被 AOP 代理,这就形成了一个循环依赖。如果没有三级缓存,Spring 就无法在ServiceA完全初始化之前创建一个代理实例来解决这个循环依赖。

现在,让我们看看三级缓存是如何解决这个问题的:

  • 三级缓存 singletonFactories:在创建ServiceA的过程中,它的工厂对象会被放入三级缓存singletonFactories。这个工厂对象负责生成ServiceA的实例。

  • 解决循环依赖:当创建ServiceB并尝试注入ServiceA时,Spring 会检查三级缓存中是否存在ServiceA的工厂对象。如果存在,Spring 使用该工厂对象来创建ServiceA的代理实例,并将其放入一级缓存singletonObjects中。这样,即使ServiceA和ServiceB相互依赖,Spring 也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

总结来说,二级缓存能够解决一些循环依赖问题,但在涉及 AOP 代理的情况下,它无法提供一个尚未初始化的 Bean 的代理实例。三级缓存通过引入工厂对象来提前创建代理实例,从而解决了这个问题。

创建 ServiceB 实例:接着,Spring 尝试创建 ServiceB 的实例,并试图注入 ServiceA。由于 ServiceA 的完整初始化尚未完成,因此无法生成其 AOP 代理。 这里为什么要生成 ServiceA 的 AOP 代理,是不是 ServiceB 要注入的是 ServiceA 的代理对象

是的,ServiceB 要注入的是 ServiceA 的代理对象。在 Spring 中,如果一个 Bean 被 AOP 代理,那么其他 Bean 注入的是这个 Bean 的代理对象,而不是原始的 Bean 实例。这样做的目的是为了让代理对象能够拦截对原始 Bean 方法的调用,以便在调用前后执行额外的操作,如日志记录、事务管理等。

在创建 ServiceB 实例时,如果需要注入 ServiceA,而 ServiceA 尚未完成初始化,就无法生成其 AOP 代理。这是因为 AOP 代理是在 Bean 的初始化阶段完成的,这意味着所有的属性填充和方法调用之后。如果 ServiceA 需要被代理,那么在代理之前,Spring 会尝试从缓存中获取到原始的 ServiceA 实例,以避免在代理过程中出现循环引用的问题。

因此,生成 ServiceA 的 AOP 代理是为了确保在 Bean 的初始化阶段能够生成代理对象,并将其注入到其他 Bean 中,从而实现横切关注点的模块化。这样,开发人员可以专注于核心业务逻辑的开发,而不必关心那些横切关注点的实现细节。

三级缓存 singletonFactories 既可以创建代理 bean 也可以创建非代理 bean

是的,三级缓存 singletonFactories 可以用于创建代理 bean 和非代理 bean。

在 Spring 中,singletonFactories 是一个 Map 类型的缓存,它存储了 Bean 的工厂对象。这些工厂对象负责生成 Bean 的实例。当需要创建一个 Bean 时,Spring 会首先检查一级缓存 singletonObjects 中是否已经存在该 Bean 的实例。如果不存在,则继续检查二级缓存 earlySingletonObjects 中是否存在半成品的 Bean 实例。如果仍然不存在,Spring 会从三级缓存 singletonFactories 中获取到对应 Bean 的工厂对象,并使用该工厂对象来创建 Bean 的实例。

对于非代理 Bean,singletonFactories 中的工厂对象直接创建 Bean 的实例并将其放入一级缓存 singletonObjects 中。而对于需要被 AOP 代理的 Bean,singletonFactories 中的工厂对象会先创建一个原始的 Bean 实例,然后通过 AOP 代理来生成代理对象,并将代理对象放入一级缓存 singletonObjects 中。

因此,三级缓存 singletonFactories 既可以创建代理 bean,也可以创建非代理 bean,根据具体的需求和配置来决定是否需要进行 AOP 代理。

标签: #JAVA 991
相关文章

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

SpringBoot整合异步任务执行 2024-10-08 11:24

同步任务: 同步任务是在单线程中按顺序执行,每次只有一个任务在执行,不会引发线程安全和数据一致性等 并发问题 同步任务需要等待任务执行完成后才能执行下一个任务,无法同时处理多个任务,响应慢,影响用 户体验 异步任务: 异步任务是在多线程中同时执行,多个任务可以并发执行,同时处理多个请求,响应快,资源

springboot kafka多数据源,通过配置动态加载发送者和消费者 2024-10-08 11:24

前言 最近做项目,需要支持kafka多数据源,实际上我们也可以通过代码固定写死多套kafka集群逻辑,但是如果需要不修改代码扩展呢,因为kafka本身不处理额外逻辑,只是起到削峰,和数据的传递,那么就需要对架构做一定的设计了。 准备test kafka本身非常容易上手,如果我们需要单元测试,引入ja

SpringBoot 集成 Redis 2024-10-08 11:24

一:SpringBoot 集成 Redis ①Redis是一个 NoSQL(not only)数据库, 常作用缓存 Cache 使用。 ②Redis是一个中间件、是一个独立的服务器;常用的数据类型: string , hash ,set ,zset , list ③通过Redis客户端可以使用多种语

SpringBoot整合QQ邮箱 2024-10-08 11:24

SpringBoot可以通过导入依赖的方式集成多种技术,这当然少不了我们常用的邮箱,现在本章演示SpringBoot整合QQ邮箱发送邮件…. 下面按步骤进行: 1.获取QQ邮箱授权码 1.1 登录QQ邮箱 1.2 开启SMTP服务 找到下图中的SMTP服务区域,如果当前账号未开启的话自己手动开启。

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.