深入理解Spring的三级缓存机制

news2024/11/15 19:31:27

个人名片
在这里插入图片描述
🎓作者简介:java领域优质创作者
🌐个人主页:码农阿豪
📞工作室:新空间代码工作室(提供各种软件服务)
💌个人邮箱:[2435024119@qq.com]
📱个人微信:15279484656
🌐个人导航网站:www.forff.top
💡座右铭:总有人要赢。为什么不能是我呢?

  • 专栏导航:

码农阿豪系列专栏导航
面试专栏:收集了java相关高频面试题,面试实战总结🍻🎉🖥️
Spring5系列专栏:整理了Spring5重要知识点与实战演练,有案例可直接使用🚀🔧💻
Redis专栏:Redis从零到一学习分享,经验总结,案例实战💐📝💡
全栈系列专栏:海纳百川有容乃大,可能你想要的东西里面都有🤸🌱🚀

目录

  • 深入理解Spring的三级缓存机制
    • 什么是三级缓存
    • 为什么需要三级缓存
      • 示例说明
    • 三级缓存的实现原理
      • 初始化流程
      • 关键代码解析
    • 三级缓存的工作流程
      • 流程图
      • 实际应用
      • 源码解析
        • `DefaultSingletonBeanRegistry` 类
        • `getSingleton` 方法
    • 总结

深入理解Spring的三级缓存机制

在Spring的源码中,有一个非常重要但常被忽视的设计——三级缓存。三级缓存是Spring解决循环依赖问题的关键机制之一,它在Spring容器初始化Bean时起到了至关重要的作用。本文将详细介绍Spring的三级缓存机制,探讨其实现原理和应用场景,并结合源码解析进一步理解其工作流程。

在这里插入图片描述

什么是三级缓存

在Spring的Bean生命周期中,三级缓存主要用于解决循环依赖问题。三级缓存分为以下三个部分:

  1. 一级缓存(singletonObjects):用于存储完全初始化好的单例Bean。
  2. 二级缓存(earlySingletonObjects):用于存储提前暴露的单例Bean,主要是为了应对循环依赖问题。
  3. 三级缓存(singletonFactories):用于存储ObjectFactory,用于在需要时创建Bean。

这三个缓存共同协作,保证了Spring能够处理复杂的Bean依赖关系。

为什么需要三级缓存

在Spring容器启动过程中,如果两个Bean相互依赖,可能会导致循环依赖问题。假设有两个Bean,A和B,A依赖于B,B又依赖于A。如果没有缓存机制,Spring容器在初始化A和B时,会陷入无限循环,最终导致StackOverflowError。三级缓存的引入正是为了解决这一问题,通过提前暴露Bean引用,保证依赖注入的顺利进行。

示例说明

假设有以下两个类:

public class A {
    private B b;

    public void setB(B b) {
        this.b = b;
    }
}

public class B {
    private A a;

    public void setA(A a) {
        this.a = a;
    }
}

A依赖B,B依赖A,这就是一个典型的循环依赖。在没有缓存机制的情况下,Spring容器会在创建A时发现需要B,但创建B时又需要A,最终陷入死循环。

三级缓存的实现原理

Spring的三级缓存主要在DefaultSingletonBeanRegistry类中实现,该类包含了以下三个关键属性:

  • singletonObjects: 一级缓存,存储完全初始化好的单例Bean。
  • earlySingletonObjects: 二级缓存,存储提前暴露的单例Bean。
  • singletonFactories: 三级缓存,存储ObjectFactory,用于在需要时创建Bean。

初始化流程

  1. 创建Bean实例
    Spring首先会尝试从一级缓存中获取Bean实例,如果没有找到,则会从二级缓存中获取,如果仍未找到,则会从三级缓存中获取。如果三级缓存中也没有,才会创建新的Bean实例。

  2. 提前暴露Bean引用
    在创建Bean实例的过程中,Spring会将创建中的Bean实例提前暴露到三级缓存中。这一步是通过将Bean包装成一个ObjectFactory来实现的,该ObjectFactory在需要时可以返回Bean实例。

  3. 处理依赖注入
    在进行依赖注入时,如果依赖的Bean存在循环引用,Spring会从三级缓存中获取提前暴露的Bean实例,以解决循环依赖问题。

  4. 完成初始化
    在完成依赖注入和其他初始化操作后,Spring会将完全初始化好的Bean实例从三级缓存中移到二级缓存,再从二级缓存移到一级缓存,确保后续可以直接从一级缓存中获取。

关键代码解析

以下是Spring源码中涉及三级缓存的关键代码片段:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

getSingleton方法中,Spring首先从一级缓存中获取Bean实例,如果没有找到,再尝试从二级缓存中获取,最后从三级缓存中获取ObjectFactory来创建Bean实例。

三级缓存的工作流程

三级缓存的工作流程可以分为以下几个步骤:

  1. 创建Bean实例
    当Spring容器开始创建一个Bean时,会首先尝试从一级缓存中获取,如果没有找到,则会创建新的Bean实例,并将该实例的ObjectFactory放入三级缓存中。

  2. 提前暴露Bean引用
    在创建Bean实例的过程中,如果发现Bean依赖其他Bean(包括自身依赖),Spring会将创建中的Bean实例提前暴露到三级缓存中,以便其他Bean可以引用。

  3. 依赖注入
    当Spring发现Bean依赖其他Bean时,会从三级缓存中获取提前暴露的Bean引用,解决循环依赖问题。

  4. 完成初始化
    在完成依赖注入和其他初始化操作后,Spring会将完全初始化好的Bean实例从三级缓存中移到二级缓存,再移到一级缓存,以确保后续可以直接从一级缓存中获取。

流程图

以下是三级缓存工作流程的简要图示:

+-------------------------+
|                         |
|    1. 创建Bean实例      |
|                         |
+-----------+-------------+
            |
            v
+-----------+-------------+
|                         |
|  2. 提前暴露Bean引用     |
|                         |
+-----------+-------------+
            |
            v
+-----------+-------------+
|                         |
|    3. 依赖注入处理       |
|                         |
+-----------+-------------+
            |
            v
+-----------+-------------+
|                         |
|    4. 完成初始化        |
|                         |
+-------------------------+

实际应用

在实际应用中,三级缓存主要用于解决循环依赖问题。例如,在一个复杂的业务场景中,如果多个服务相互依赖,三级缓存可以保证Spring容器能够顺利初始化所有服务,避免循环依赖导致的错误。

源码解析

我们以Spring的源码为例,进一步解析三级缓存的实现。

DefaultSingletonBeanRegistry

DefaultSingletonBeanRegistry是三级缓存机制的核心类。以下是该类的部分源码:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    // other methods...
}

DefaultSingletonBeanRegistry类中,singletonObjects是一级缓存,earlySingletonObjects是二级缓存,singletonFactories是三级缓存。

getSingleton 方法

getSingleton方法是获取单例Bean实例的核心方法,以下是该方法的简化版源码:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

getSingleton方法中,Spring首先尝试从一级缓存(singletonObjects)中获取Bean实例。如果没有找到,并且当前Bean正在创建中(循环依赖的情况),则尝试从二级缓存(earlySingletonObjects)中获取。如果二级缓存中也没有找到,并且允许提前引用(allowEarlyReferencetrue),则从三级缓存(singletonFactories)中获取ObjectFactory并创建Bean实例。

总结

通过本文的详细解析,我们了解了Spring的三级缓存机制及其在解决循环依赖问题中的重要作用。三级缓存包括一级缓存、二级缓存和三级缓存,它们分别用于存储完全初始化好的单例Bean、提前暴露的单例Bean以及ObjectFactory。三级缓存共同协作,保证了Spring容器能够处理复杂的依赖关系,顺利初始化所有Bean。

三级缓存的引入有效解决了循环依赖问题,使得Spring容器在处理复杂的依赖关系时更加灵活和高效。在实际应用中,理解并掌握三级缓存机制,可以帮助我们更好地解决Bean的依赖注入问题,提升Spring应用的稳定性和可维护性。希望本文能够帮助读者深入理解Spring的三级缓存机制,并在实际开发中应用这一强大的工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1990119.html

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

相关文章

Ubuntu(20.04 LTS)更换镜像源

此换镜像源方法只适用x86_64架构的系统&#xff0c;其他架构的系统参考ubuntu-ports的方法 1、备份文件 sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk2、创建新文件 sudo vi /etc/apt/sources.list根据自己系统版本选择下面对应的镜像源添加到新文件中&#xf…

智能指针--

智能指针简介 头文件&分类 智能指针都在memory中&#xff0c; 有auto_ptr, unique_ptr, shared_ptr, weak_ptr 智能指针发展史 C&#xff0b;&#xff0b;98就有智能指针了&#xff08;auto_ptr&#xff09; c&#xff0b;&#xff0b;11前&#xff0c;智能指针主要靠bo…

FPGA开发——在Quartus中实现对IP核的PLL调用

一、简介 PLL主要由鉴相器&#xff08;PD&#xff09;、环路滤波器&#xff08;LF&#xff09;和压控振荡器&#xff08;VCO&#xff09;三部分组成。鉴相器检测输入信号与VCO输出信号的相位差&#xff0c;并输出一个与相位差成正比的电压信号。该信号经过环路滤波器滤除高频噪…

esp32学习笔记

前言&#xff1a;学习视频链接放在最后&#xff0c;开发方式为esp32Arduino&#xff0c;使用型号为ESP32-WROOM-32&#xff0c;引脚功能分配图如下。 #esp32介绍 GPIO的引脚默认情况下&#xff0c;只能当做普通功能引脚使用&#xff0c;也就是只能输入&#xff0c;输出&#x…

git 常用指令(创建分支、提交分支、解决冲突)

1. 初始化git 将你的代码放入你创建的文件中&#xff0c;执行 git init(前提你电脑安装过git哈)2. 查看当前项目git 状态 git status 3. 将代码添加到暂存区 git add . &#xff08;提交所有修改的代码&#xff0c;如果向指定提交使用&#xff1a;git add <文件名>&am…

SQL语句创建数据库(增删查改)

SQL语句 一.数据库的基础1.1 什么是数据库1.2 基本使用1.2.1 连接服务器1.2.2 使用案例 1.2 SQL分类 二.库的操作2.1 创建数据库2.2 创建数据库示例2.3 字符集和校验规则2.3.1 查看系统默认字符集以及校验规则2.3.2查看数据库支持的字符集2.3.3查看数据库支持的字符集校验规则2…

【RTOS面试题】ISR中可以使用互斥锁和信号量吗?

在中断服务程序&#xff08;ISR, Interrupt Service Routine&#xff09;中直接使用互斥锁&#xff08;mutex&#xff09;和信号量&#xff08;semaphore&#xff09;是有风险的&#xff0c;因为这些同步机制通常不是中断安全的。但是&#xff0c;可以通过一些方法来安全地在 I…

QWT+Qt Creator+MSVC的配置与使用

目录 一、介绍 二、QWT下载 三、QWT编译 3.1 设置构建套件 3.2 修改QWT相关文件 3.3 进行QWT编译 四、QWT配置 4.1 配置QWT的lib文件 4.2 配置QWT的dll文件 4.3 配置QWT的designer的dll文件 五、代码实验 一、介绍 QWT&#xff0c;全称是Qt Widgets for Technical…

Python 异步编程:Asyncio 实现原理

常见的并发模型 多进程/多线程异步ActorPub/Sub Python 异步的基石&#xff1a;协程 协程简介 概念&#xff1a;协作式多任务的子程序&#xff0c;用户态线程或微线程&#xff08;Coroutine&#xff09;。 特点&#xff1a;子程序执行可以中断&#xff0c;恢复后不会丢失之…

uniapp 荣耀手机 没有检测到设备 运行到Android手机 真机运行

背景&#xff1a; 使用uniapp框架搭建的项目&#xff0c;开发的时候在浏览器运行&#xff0c;因为项目要打包成App&#xff0c;所以需要真机联调&#xff0c;需要运行到Android手机&#xff0c;在手机上查看/运行项目。通过真机调试才能确保软件开发的准确性和页面显示的完整性…

mac 2k显示器 配置

前言 今年5月份买了一个2k显示器&#xff0c;刚收到的时候发现只有一个1080 x 720&#xff08;HiDPI&#xff09;分辨率是人眼看起来比较舒服的&#xff0c;于是一直用着。但是直到开始写前端代码的时候&#xff0c;我才发现&#xff0c;网页在2k显示器和内建显示器的布局竟然…

Python 循环引用与内存泄漏:深度解析

Python 循环引用与内存泄漏&#xff1a;深度解析 在Python编程中&#xff0c;循环引用和内存泄漏是两个需要特别注意的问题。本文将深入探讨Python中的循环引用现象、其导致的内存泄漏问题&#xff0c;并提供详细的解决思路与方法。同时&#xff0c;我们还将分析一些常见场景&…

【CSDN平台BUG】markdown图片链接格式被手机端编辑器自动破坏(8.6 已修复)

文章目录 bug以及解决方法bug原理锐评后续 bug以及解决方法 现在是2024年8月&#xff0c;我打开csdn手机编辑器打算修改一下2023年12月的一篇文章&#xff0c;结果一进入编辑器&#xff0c;源码就变成了下面这个样子&#xff0c;我起初不以为意&#xff0c;就点击了发布&#…

【IoT NTN】面向 5G/6G 卫星:NTN 标准发展、关键技术与未来思考

目录 国际标准化进展 架构及关键技术 5GNTN组网架构 关键技术 1、时频同步技术 2、覆盖增强技术 3、移动性管理技术 4、混合自动重传请求技术 5、自适应调制与编码技术 挑战与潜在解决方案 星地协同全域覆盖模型 星地协同多维资源调度 星地协同多层卫星路由 星地…

网络主播进入国家职业分类!1500万主播将有新身份

在营销策划界摸爬滚打十多年的我&#xff0c;最近可是被一则劲爆消息给震撼到了——国家正式官宣了19个全新职业&#xff0c;这可是职场版图的一次大扩张啊&#xff01; 其中最让人眼前一亮的是&#xff0c;网络主播竟然堂而皇之地登上了新职业的大舞台&#xff0c;正式“转正…

【多线程-从零开始-陆】wait、notify和notifyAll

线程饿死 一个或多个线程因为无法获得执行所需的资源&#xff08;如CPU时间、锁、或其他同步控制&#xff09;而被长时间阻塞或延迟执行的情况。尽管这些线程可能处于可执行状态并且已经准备好运行&#xff0c;但由于资源分配的不均衡或调度策略的问题&#xff0c;它们无法获得…

快讯 | 单卡4090显卡即可解锁视频生成,智谱AI CogVideoX模型开源!

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

认识RAID磁盘阵列

文章目录 1、RAID磁盘阵列介绍2、多种RAID级别RAID 0 &#xff08;条带化存储&#xff09;RAID 1 &#xff08;镜像存储&#xff09;RAID 5RAID 6RAID 10&#xff08;先做镜象&#xff0c;再做条带&#xff09;RAID 01&#xff08;先做条带&#xff0c;再做镜象&#xff09;区别…

leetcode日记(64)最小覆盖子串

很复杂的题目&#xff0c;无论是思路还是实践都很难… 思路还是看了答案&#xff08;&#xff1f;&#xff09;设定两个指针“框”出一串字符串&#xff0c;初始两个指针都指在s的零位&#xff0c;先移动下指针&#xff0c;直到使框出的字符串中包含t中所有字符串&#xff0c;…

c# 排序、强转枚举

List<Tuple<double,int>> mm中doble从小到大排序 mm本身排序 在C#中&#xff0c;如果你有一个List<Tuple<double, int>>类型的集合mm&#xff0c;并且你想要根据Tuple中的double值&#xff08;即第一个元素&#xff09;从小到大进行排序&#xff0c;同…