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

news2024/11/18 8:59:27

在这里插入图片描述

文章目录

    • 一、 三级缓存的概述
    • 二、 三级缓存的实现原理
      • 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<>();

    @Override
    public void addUser(String username) {
        users.add(username);
    }
}

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

/*
* 定义了一个配置类 AppConfig,在其中通过 @Bean 注解定义了一个名为 userService 的 Bean,并指定了它的实现类为 UserServiceImpl
*/
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    @Bean
    public 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.coloradmin.cn/o/1610331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马---苍穹外卖总结上

1.功能模块 1.1 管理端功能 员工登录/退出 , 员工信息管理 , 分类管理 , 菜品管理 , 套餐管理 , 菜品口味管理 , 订单管理 &#xff0c;数据统计&#xff0c;来单提醒。 模块描述登录/退出内部员工必须登录后,才可以访问系统管理后台员工管理管理员可以在系统后台对员工信息进…

数据结构学习--环形链表

环形链表 我们在判断一个链表是否是环形的&#xff0c;即首尾相连&#xff0c;我们可以以使用快慢指针&#xff0c;如果快指针能再次追上慢指针&#xff0c;就说明该链表是环形的&#xff0c;这边可以举个操场跑步的例子&#xff0c;当操场是环形的&#xff0c;跑的快的&#…

Ansible组件说明

1.Ansible Inventory 工作当中有不同的业务主机&#xff0c;我们需要在把这些机器信息存放在inventory里面&#xff0c;ansible默认的inventory的文件是/etc/ansible/hosts&#xff0c;也可以通过ANSIBLE_HOSTS环境变量来指定或者运行ansible和ansible-playbook的时候用-i参数临…

泛微E9开发 快速隐藏明细表列

快速隐藏明细表列 1、隐藏列方法&#xff08;不作用&#xff0c;一直隐藏&#xff09; 在实际运用中&#xff0c;用户不需要但是需要间接使用的列&#xff0c;我们可以通过右击该列-【列自定义属性】-在“列自定义属性”菜单中启用“隐藏列”功能。 根据该方法设置的前端页…

纯js图片上传插件

目录标题 一、效果预览二、使用简单三、完整代码&#xff08;一&#xff09;index.html&#xff08;二&#xff09;css&#xff08;三&#xff09;js四、附带后台上传文件代码 一、效果预览 支持多图片上传&#xff0c;删除、预览。 二、使用简单 导入依赖&#xff08;需要…

视觉化数据的艺术:掌握Matplotlib基础绘图

Matplotlib基础绘图 文章目录 Matplotlib基础绘图1.基础流程2.绘图设置3.动态rc参数4.pyplot的基础图表函数 安利 1.基础流程 1.导入模块。绘图之前&#xff0c;需要先导入包含相应函数的模块。 2.创建画布与创建子图。第一部分主要是构建出一张空白的画布&#xff0c;如果需要…

Elastic 网络爬虫:为你的网站添加搜索功能

作者&#xff1a;来自 Elastic Lionel Palacin 为了演示如何使用 Elastic 网络爬虫&#xff0c;我们将以一个具体的网站为例&#xff0c;讲解如何在该网站上添加搜索功能。我们将探讨发现网站的方法&#xff0c;并利用 Elastic 网络爬虫提供的功能&#xff0c;以最佳方式准备待…

springboot+ssm基于Java的小型企业银行账目管理系统代码Lw

本毕业设计课题要求学生基于软件工程方法&#xff0c;根据课题的需求&#xff0c;给出小型企业银行账目管理系统概要设计、详细设计、数据库的设计以及系统实现和测试&#xff0c;并撰写规范的毕业设计说明书。该系统的主要模块有&#xff1a;系统管理、帐目管理、查询统计、用…

如何创建响应式HTML电子邮件模板

在这个适合初学者的指南中&#xff0c;你将学习如何创建一个响应式电子邮件模板。你将跟随逐步说明以及代码片段设计一个在任何设备上都看起来很棒的电子邮件模板。 这个项目非常适合渴望掌握电子邮件设计基础的新手&#xff01; &#xff08;本文视频讲解&#xff1a;java56…

抖店严查违规店铺!老商家下台,新手正是入驻时机!

哈喽~我是电商月月 抖店一直在严查违规店铺&#xff0c;很多新手都在犹豫不前&#xff0c;不知道要不要去做抖音小店 以为是抖音平台在“想方设法”克扣商家钱财&#xff0c;开店都是坑&#xff1f; 要这样想&#xff0c;那就大错特错了&#xff01; 抖店整改&#xff0c;把…

你的mongodb客户端是哪个呢?

MongoDB 是一种流行的文档数据库&#xff0c;它可以支持多种场景和应用。有很多客户端工具可以用来管理和操作 MongoDB&#xff0c;以下是一些常用的工具&#xff0c;以及它们的介绍&#xff1a; 一、MongoDB Shell MongoDB Shell 是连接&#xff08;和使用&#xff09;MongoD…

每日一题:计数质数

给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;4 解释&#xff1a;小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。示例 2&#xff1a; 输入&#xff1a;n 0 输出&#xff1a;0示例 3&#…

NVIDIA智算中心“产品”上市,AI工业革命的iPhone时刻

GTC 2024落下帷幕了&#xff0c;但这个大会的信息仍在AI产业和经济中发酵。咨询机构WIKIBON认为&#xff0c;GTC 2024在整个科技史中的意义超过了当年史蒂夫乔布斯的iPod和iPhone发布。在AI将永久改变人类的共识下&#xff0c;GTC 2024在广度、愿景、生态系统等方面都有着深远影…

危险场景智能运维巡检系统

在石油、天然气、煤炭和化工等行业&#xff0c;特别是在I/IIC级防爆区场景中&#xff0c;存在着诸如易燃、易爆、高温、有毒有害以及粉尘等危险因素。例如&#xff0c;油气转运站、催化裂化装置、煤化工甲醇车间以及制氢站等地点&#xff0c;都面临着这些潜在的危险。传统的人工…

如何使用ArcGIS Pro进行路径分析

路径分析是一种空间分析技术&#xff0c;用于确定两个或多个地点之间最佳路径或最短路径&#xff0c;这里为大家介绍一下在ArcGIS Pro中如何进行路径分析&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的道路数据&#xff0c;除了道路数据&a…

交换基础配置--单臂路由

1、创建vlan 创建vlan10 创建vlan10和vlan20 创建vlan1到vlan9 vlan1可以不用创建&#xff0c;因为交换机的所有接口默认为vlan1 本实验只需要vlan10和vlan20&#xff0c;以上只是介绍创建vlan的方法。 查看创建的vlan&#xff1a; sw2同理。接着将需要划分vlan的接口划入…

linux基本命令2

一、Whereis是查找工具中速度最快的一个&#xff0c;但是whereis查找的范围有限&#xff0c; whereis只能查找2进制程序&#xff0c; 使用whereis查找 ls -b&#xff1a;查找2进制程序 -m&#xff1a;查找文档 -s&#xff1a;查找码源 二、which命令是查找工具中速度最快命令…

3D模型查看器开发实战【WebGL】

本文介绍如何从头开发一个包含3D 模型查看器的页面 - 尽管它非常简单&#xff0c;但你将学习的步骤也应该有助于构建其他类型的 Web 应用程序。 在自己的网站或博客里展示3D模型更简单的方式是使用NSDT 3DConvert提供的在线服务&#xff0c;无需任何开发工作&#xff0c;5分钟…

access多表关联提示:语法错误(操作符丢失)在查询表达式中

在access数据库中执行多表关联时提示了一个错误 select * from Patient a inner join BioMain b on a.BioIDb.BioID inner join BioResult c on b.BioIDc.BioID where len(a.PatientID)>12 and b.AddTime>#2024-04-17 05:53:23# and b.AddTime<#2024-04-17 17:53:23#…

基于Python 实现数据可视化大屏

数据本身是冰冷的数字&#xff0c;通过选择合适的图形或者图表来进行展示表达&#xff0c;使得传递给使用者的感受更加直观、更容易获得其中的价值。 数据可视化将技术与艺术完美结合&#xff0c;借助图形化的手段&#xff0c;清晰有效地传达与沟通信息。一方面&#xff0c;数…