深度解析 Spring 源码:三级缓存机制探究

news/2024/5/19 7:51:25

在这里插入图片描述

文章目录

    • 一、 三级缓存的概述
    • 二、 三级缓存的实现原理
      • 2.1 创建Bean流程图
      • 2.2 getBean()
      • 2.3 doGetBean()
      • 2.4 createBean()
      • 2.5 doCreateBean()
      • 2.4 getSingleton()
    • 三、 三级缓存的使用场景与注意事项
      • 3.1 在实际开发中如何使用三级缓存
      • 3.2 三级缓存可能出现的问题及解决方法

一、 三级缓存的概述

概念:

三级缓存是指用于管理 Bean 对象创建过程中不同阶段的缓存机制。

  1. 一级缓存(singletonObjects):存储已经完全初始化的单例 Bean 对象。
  2. 二级缓存(earlySingletonObjects):存储已经实例化但尚未完全初始化的单例 Bean 对象。
  3. 三级缓存(singletonFactories):存储 Bean 对象的创建工厂,用于在创建过程中检测循环依赖。

意义:

  1. 提高性能: 通过缓存已经创建的 Bean 对象,Spring 可以在后续的请求中直接返回缓存的对象,避免重复创建,从而提高了系统的性能和响应速度。
  2. 解决循环依赖: 三级缓存中的三级缓存(singletonFactories)用于解决循环依赖问题。当 A Bean 依赖于 B Bean,而 B Bean 又依赖于 A Bean 时,Spring 可以在创建 A Bean 的过程中将其提前放入三级缓存,以解决循环依赖的问题。
  3. 实现懒加载: 二级缓存(earlySingletonObjects)可以实现 Bean 的懒加载,即在 Bean 第一次被请求时才进行初始化,而不是在容器启动时就立即创建所有的 Bean 对象。
  4. 保证单例: 通过一级缓存(singletonObjects)中存储的已初始化的单例 Bean 对象,Spring 可以保证在应用程序中只存在一个实例,实现了单例模式的效果。

二、 三级缓存的实现原理

由于创建Bean的代码量非常多,本文仅展示与三级缓存有关的代码。

读者想要对三级缓存了解更加深刻,可以自行创建循环依赖的Bean,根据调试来解读代码。

2.1 创建Bean流程图

这里仅仅展示创建Bean的大致流程图,想深入了解的读者可以自行解读源码。

在这里插入图片描述

2.2 getBean()

在这里插入图片描述

2.3 doGetBean()

在这里插入图片描述

在这里插入图片描述

2.4 createBean()

在这里插入图片描述

2.5 doCreateBean()

在这里插入图片描述

在这里插入图片描述

2.4 getSingleton()

在这里插入图片描述

在这里插入图片描述

三、 三级缓存的使用场景与注意事项

3.1 在实际开发中如何使用三级缓存

在实际开发中,我们通常不需要直接操作 Spring 的三级缓存,因为 Spring 框架会自动管理这些缓存。只需要通过合适的配置和编码实践来确保 bean 的正确创建和管理即可。

在实际开发中使用 Spring 的三级缓存简单Demo

假设有一个简单的服务接口 UserService 和其实现类 UserServiceImpl,将使用 Spring 来管理它们的依赖注入单例管理

public interface UserService {void addUser(String username);
}public class UserServiceImpl implements UserService {private List<String> users = new ArrayList<>();@Overridepublic void addUser(String username) {users.add(username);}
}

现在配置 Spring 容器并将 UserServiceImpl 注入到容器中:

/*
* 定义了一个配置类 AppConfig,在其中通过 @Bean 注解定义了一个名为 userService 的 Bean,并指定了它的实现类为 UserServiceImpl
*/
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {@Beanpublic UserService userService() {return new UserServiceImpl();}
}public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// Spring 默认情况下会使用单例模式管理 Bean,所以每次获取 UserService Bean 都会得到同一个实例// 从 Spring 应用上下文中获取一个名为 userService 的 Bean,其类型为 UserService。在 AppConfig 中定义了 userService Bean,并且它的实现类为 UserServiceImpl,所以这里实际上是获取了一个 UserServiceImpl 类型的对象UserService userService1 = context.getBean(UserService.class);userService1.addUser("Alice");// 再次从 Spring 应用上下文中获取一个 UserService 类型的 Bean,实际上是获取了之前已经创建过的同一个 UserServiceImpl 类型的对象UserService userService2 = context.getBean(UserService.class);userService2.addUser("Bob");// 可给对象做一些其它额外的操作// 关闭了 Spring 应用上下文,释放资源context.close();}
}

分析 Spring 是如何使用三级缓存的:

  1. BeanDefinition 缓存: 在 Spring 容器启动时,Spring 会解析所有的 bean 定义(BeanDefinition),并将其缓存起来。这个缓存存储了 bean 的元数据信息,如类名、依赖关系等。当我们通过 @Bean 或者其他方式声明一个 bean 时,Spring 首先会检查 BeanDefinition 缓存中是否存在该 bean 的定义,如果存在,则直接使用该定义,否则会根据配置信息创建一个新的 BeanDefinition。
  2. 单例对象实例缓存: 当我们通过 Spring 容器获取一个单例 bean 时,Spring 首先会检查单例对象实例缓存中是否已经存在该 bean 的实例。如果存在,则直接返回缓存中的实例,否则会根据 BeanDefinition 中的信息创建一个新的 bean 实例,并放入缓存中。
  3. 早期的单例对象实例缓存: 在 bean 的创建过程中,Spring 可能需要解决循环依赖等问题。为了解决这些问题,Spring 使用了早期的单例对象实例缓存。当 bean 的创建过程中遇到循环依赖时,Spring 会将正在创建的 bean 提前暴露给其他需要它的 bean,从而解决循环依赖的问题。

3.2 三级缓存可能出现的问题及解决方法

在 Spring 中,虽然没有官方定义的 “三级缓存” 概念,但可以类比于 MyBatis 中的缓存机制。

从 Spring 的缓存相关模块(如 Spring Cache)的角度来看,也存在一些可能的问题以及解决方法。

  1. 缓存数据不一致性问题
    • 问题:当使用 Spring Cache 缓存方法的返回结果时,如果在缓存数据过期前发生了数据变更(如数据库更新),则缓存中的数据与实际数据不一致。
    • 解决方法:
      • 手动清除缓存:在数据变更操作后,手动清除相应的缓存,保持缓存与数据库数据的一致性。
      • 使用缓存刷新策略:在 Spring Cache 中,可以通过配置缓存刷新策略,定期或在特定触发条件下刷新缓存,使缓存中的数据保持最新。
  2. 缓存击穿问题
    • 问题:当某个缓存项过期时,同时有大量并发请求访问该缓存项,可能导致大量请求直接访问底层数据源(如数据库),增加系统负载。
    • 解决方法:
      • 设置合适的缓存过期时间:根据业务场景和系统负载情况,设置合适的缓存过期时间,避免大量缓存同时失效。
      • 使用互斥锁机制:在缓存项失效时,使用互斥锁机制保证只有一个线程能够重新加载缓存项,其他线程等待该线程加载完数据后再从缓存中获取。
  3. 缓存雪崩问题
    • 问题:当大量缓存项同时失效时,可能导致大量请求直接访问底层数据源,从而造成系统压力过大。
    • 解决方法:
      • 使用分布式缓存:如果系统是分布式的,考虑使用分布式缓存技术,将缓存分布在多个节点上,避免单点故障。
      • 设置随机过期时间:在设置缓存过期时间时,可以稍微随机一些,避免大量缓存同时失效。
      • 使用熔断机制:当系统压力过大时,可以使用熔断机制暂时关闭对底层数据源的访问,避免系统崩溃。

今是生活,今是动力,今是行为,今是创作


http://www.mrgr.cn/p/75841307

相关文章

股票数据爬虫

东方财富网-数据中心 —— 爬虫项目 0x00 起因 MaMa 看到别人有个软件,可以直接把一个网站上的数据全部爬进一个 Excel 里边,但是那个人不给这个软件,所以她怂恿我写一个。。。 0x01 需求 千股千评 _ 数据中心 _ 东方财富网 (eastmoney.com) 对于里边的00~60开头的股票,把股…

Linux多进程(一)创建进程与进程控制

一、进程状态 进程一共有五种状态分别为&#xff1a;创建态&#xff0c;就绪态&#xff0c;运行态&#xff0c;阻塞态(挂起态)&#xff0c;退出态(终止态)其中创建态和退出态维持的时间是非常短的&#xff0c;稍纵即逝。主要是就绪态, 运行态, 挂起态三者之间的状态切换。 就绪…

中北大学软件学院操作系统实验二进程调度算法

实验时间 2024年 4 月13日14时至16时 学时数 2 1.实验名称 实验二进程调度算法 2.实验目的 (1)加深对进程的概念及进程调度算法的理解&#xff1b; (2)在了解和掌握进程调度算法的基础上&#xff0c;编制进程调度算法通用程序&#xff0c;将调试结果显示在计算机屏幕上&am…

Python学习1--变量和简单数据类型

本章练习&#xff1a; Python之禅&#xff1a;

【实用技巧】JSON格式转换方式

1 前言 对接开发中,常遇到的就是报文转换。比如从淘宝或者京东拉取订单,亦或是各个公司内部的WMS、OMS等交互,都涉及到格式转换。而大多的格式基本上都是 JSON 格式,当然也有一些老的 SAP 交互用的是 XML格式的,还有一小部分 webService 接口也是用的 XML 格式。那我们这…

pwn知识——劫持__malloc_hook(在加入tcache以后)

导论 动调是最好的导师! malloc_hook函数解析 malloc_hook是malloc的钩子函数,在执行malloc时,会先检测__malloc_hook的值,如果malloc_hook的值存在,则执行该地址(值里边表现为十六进制,可以成为地址),也就是说,如果我们成功劫持malloc_hook以后并修改它的值为one_ga…

小程序如何优化搜索排名,获取曝光

在移动互联网时代&#xff0c;小程序以其便捷、轻量级的特点&#xff0c;逐渐成为用户获取服务的重要渠道。然而&#xff0c;小程序数量众多&#xff0c;如何让自己的小程序在搜索中脱颖而出&#xff0c;获取更多的曝光和流量&#xff0c;成为众多开发者关注的焦点。 一、理解…

实验二——需求分析

一、实验题目 :需求分析 二、实验目的 1、掌握StarUML软件的安装; 2、掌握利用StarUML工具分析、设计、绘制用例图; 3、掌握利用StarUML工具分析、设计、绘制类图; 4、掌握利用StarUML工具分析、设计、绘制状态图; 5、掌握利用StarUML工具分析、设计、绘制顺序图。 6、掌…

用户行为分析模型实践(四)—— 留存分析模型

作者&#xff1a;vivo 互联网大数据团队- Wu Yonggang、Li Xiong 本文是vivo互联网大数据团队《用户行为分析模型实践》系列文章第4篇 -留存分析模型。 本文详细介绍了留存分析模型的概念及基本原理&#xff0c;并阐述了其在产品中具体实现。针对在实际使用过程问题&#xff0…

android学习笔记(二)

1、自定义View。 package com.example.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; //可以在View测量和布局完成后…

计算机网络:MAC地址 IP地址 ARP协议

计算机网络&#xff1a;MAC地址 & IP地址 & ARP协议 MAC地址IP地址ARP协议 MAC地址 如果两台主机通过一条链路通信&#xff0c;它们不需要使用地址就可以通信&#xff0c;因为连接在信道上的主机只有他们两个。换句话说&#xff0c;使用点对点信道的数据链路层不需要使…

CF1535F String Distance

\(CF1535F\ \ String\ Distance\) 题意 给 \(n\) 个长度均为 \(len\) 的字符串 \(T_1,T_2,\dots T_n\),定义 \(f(a,b)\) 为将 \(a,b\) 排序后相等的最小排序次数,若无解则为 \(1337\)(这好像是个黑客用语)。求 \[\sum _{i=1}^{n}\sum _{j=i+1}^{n}f(T_i,T_j) \]其中 \[n\tim…

连接mysql -- host is not allowed to connect to this mysql server的解决

今天通过navicat连接服务器的MySQL,报错:host is not allowed to connect to this mysql server去网上搜了一摞,有些方法不太管用,踩了点坑,在此记录下。版本:MYSQL 8.0.36, CentOS 7mysql -u root -p use mysql; select user, host from user;这时候可以看到:只允许lo…

单向循环链表的初体验

单向循环链表经过小白今天一天的学习,感觉就是在单向链表的尾结点加了一个首结点的地址,不再指向NULL,个人理解就像一群孩子围成了一个圆,头尾相连,同时多少个孩子就是多少个结点,如击鼓传花一般一个个将手上的手绢传递给下一个人,一遍下来就像是单向循环的遍历,想要到…

Modbus转Profinet网关接称重设备与工控机通讯

Modbus转Profinet网关(XD-MDPN100)是一种能够实现Modbus协议和Profinet协议之间转换的设备。Modbus转Profinet网关可提供单个或多个RS485接口,使得不同设备之间可以顺利进行通信,进一步提升了工业自动化程度。Modbus转Profinet网关接称重设备与工控机通讯Modbus转Profinet网…

逆向修改app就可以游戏充值到账?

hello ,大家好, 现在市场仍然流行着非常多的传奇类游戏私服或者其他类型的游戏私服,随着私服越来越多(很多并不合法),越来越多的人加入了破解,逆向修改,或者代充的队伍并从中获利。这里我给大家分享一下这些做代充的常规的做法,以及大家作为游戏服务器如何避坑做强校验…

Linux之安装Nginx

目录 传送门前言一、快速安装二、反向代理语法1、基本语法2、location语法1. 基本语法2. 匹配规则3. 修饰符4. 权重5. 嵌套location6. 其他指令7.案例 三、配置反向代理 传送门 SpringMVC的源码解析&#xff08;精品&#xff09; Spring6的源码解析&#xff08;精品&#xff0…

构建Python中的分布式日志系统:ELK与Fluentd的结合

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在现代软件开发中&#xff0c;日志系统是至关重要的组成部分。它们不仅用于故障排查和性能监…

Linux问题集合

Linux问题集合 1. Linux下如何定位死锁? 如果你想排查你的 Java 程序是否死锁,则可以使用 jstack 工具,它是 jdk 自带的线程堆栈分析工具。 在 Linux 下,我们可以使用 pstack + gdb 工具来定位死锁问题。 pstack 命令可以显示每个线程的栈跟踪信息(函数调用过程),它的使…

2023中国企业敏捷实践白皮书发布,免费下载

在人工智能技术飞速发展,组织面临的复杂性和多变性不断加剧的背景下,《2023中国企业敏捷实践白皮书》通过广泛的调查,洞察剧变之下,谁在逆流而上,如何逆流而上。《2023中国企业敏捷实践白皮书》发布!免费下载在人工智能技术飞速发展,组织面临的复杂性和多变性不断加剧的…