通用缓存框架,spring缓存模块原理分析篇

2023-10-30

    在设计自己的缓存框架之前,有必要了解一下spring的cache模块。在spring3.1及以后的版本中,提供了基于注解的缓存支持,但spring并没有对缓存进行具体实现(除了提供一个简单的基于Map的实现之外)。本框架就是在此基础上进行扩展。

1spring通过注解操作缓存的使用方法与示例:

    因为篇幅有限,网上有很多现成的使用例子,这里不介绍spring缓存具体使用方法。

2spring通过注解操作缓存的原理:

     spring能通过注解操作缓存,是因为它采用天生就具有的强大的AOP机制,拦截业务方法。总体流程是:当一个方法上配置了Cacheable之类的注解后,这个方法被调用时,就会被一个叫CacheInterceptor的拦截器拦截,进入该类的invoke()方法中,如果当前context已经初始化完成,该方法紧接着会调用execute()。execute()方法中会读取原来被调用业务方法上的注解信息,通过这些信息进行相应的缓存操作,再跟据操作的结果决定是否调用原方法中的业务逻辑。这就是spring通过注解操作缓存的总体流程。

     CacheInterceptor是在srping上下文初始化的时候,通过配置文件中的<cache:annotation-driven />标签注册到context中的。此标签的cache名称空间对应的处理类的是org.springframework.cache.config. CacheNamespaceHandler,其中对于cache:annotation-driven标签的解析类是org.springframework.cache.config. AnnotationDrivenCacheBeanDefinitionParser(在CacheNamespaceHandler类的代码中可以找到),在这个类的parse()解析方法中跟据annotation-driven标签中的model属性决定是通过代理的方式实现 aop还是通过aspectj的方式进行切面拦截(默认采用proxy代理方式)。对于代理方式,会调用registerCacheAdvisor()注册缓存Advisor。在这个方法中会注入一个CacheInterceptor类型的拦截器。这就实现了对业务方法的切面拦截。

    在registerCacheAdvisor()方法中,还会调用一个叫parseCacheResolution()方法来注入一个缓存解析器CacheResolver,如果<cache:annotation-driven />标签中有cache-resolver的配置,就跟据配置注入一个CacheResolver,否则,就默认注入一个SimpleCacheResolver类型的实例。每个CacheResolver中包装了一个CacheManager,这个CacheManager可通过<cache:annotation-driven />标签中cacheManager之类的配置进行指定,如果没有指定,会自动注入一个叫cacheManager的bean。

     CacheInterceptor在执行execute()的过程中会调用事先注入的CacheResolver实例的resolveCaches()方法解析业务方法中需要操作的缓存Cache列表(resolveCaches()方法内部实现是通过调用此CacheResolver实例中的cacheManager属性的getCache()方法获取Cache)。获取到需要操作的Cache列表后,遍历这个列表,然后都过调用doGet(Cache cache)或doPut(Cache cache)方法进行缓存操作。doGet()或doPut()方法在CacheInterceptor类的父类AbstractCacheInvoker中定义(注意这里的Cache列表,是spring包装了特定厂商缓存后的Cache对像,是org.springframework.cache.Cache类型的实例)。

    总体上说,CacheInterceptor的execute()中对缓存的操作就是通过事先注一个CacheResolver和CacheManager实例,然后通过调用这个CacheResolver实例的resolveCaches()获得需要操作的Cache列表,再遍历列表,将每个Cache实例作为参数传入doGet()或doPut()来实现缓存读取。当然,还需要一些Key之类的参数,这个是由keyGenerator自动生成的。对于keyGenerator,这里不再介绍。看看源码就很容易理清思路。

3spring缓存模块的类简介:

     spring缓存模块对可用的Cache采用适配器模式进行了统一的封装。具体代码在spring-context-xxx.jar包中的org.springframework.cache.Cache接口,此接口声明一些储如get(Object key),put(Object key,Object value)等缓存操作的统一api。在这个接口中,还声明了一个叫getNativeCache()的方法,返回它适配的具体的缓存实现(比如在集成ehcache时,这个接口实现类的实例调用getNativeCache()时会返回net.sf.ehcache.Cache类型的实例)。每一个Cache实例都通过名称加以区分,所以在Cache接口中,还声明了一个getName()返回此实例的名称。spring提供一个叫ConcurrentMapCache的基于Map的Cache实现类,作为它内置的本地缓存实现方案。

Cache相关的类图如下:

    所有被包装的Cache,都由CacheManager实例进行统一管理(在上文的原理分析中可以看到,在<cache:annotation-driven />标签的解析过程中会自动注入一个CacheManager实例),他提供一个叫getCache(String name)的方法,跟据名称获得一个被包装的Cache。

     在Srping的缓存模块中,spring-context-xxx.jar包中自带一个叫ConcurrentMapCacheManagr的简单实现类,它可以管理上文的提到的ConcurrentMapCache类。开发人员如果配置了此管理器,也就拥有了本地缓存的能力。另外,为了让应用支持同时存在多个CacheManager,spring提供了一个CompositeCacheManager的实现类,以组合设计模式的方式统一管理多个CacheManager实例。

CacheManager部分类图如下:

    上图中,CacheManager接口中只有两个方法getCache(String)和getCacheNames(),显然,这两个方法的作用就是跟据名称获得Cache实例以及获得所有被管理的缓存的名称列表。

     上图中CompositeCacheManager与ConcurrentMapCacheManager类的作用在前文已经介绍过了。这里简单再说一下具体实现的方式:CompositeCacheManager中维护一个CacheManager列表,用户可以通过配置,把多个CacheManager配置到这个列表中,使得应用可以同时管理多个缓存管理器。这个类对于getCache(String)方法实现是通过遍历这个列表,匹配出name相同的Cache实例并返回。这个类还可以通过配置指定一个boolean的fallbackToNoOpCache标志属性,它的作用就是,当通过getCache(string)获取不到Cache实例时,是否不进行任何缓存操作。在默认情况或者fallbackToNoOpCache值为false时,在通过getCache(string)获取不到Cache实例时,业务层上可能会抛出运行时异常(比如提示“找不到XXX名称的Cache”)。但如果为true时,这时候不进行任何缓存操作也不抛异常,这种场景主要用于在不具备缓存条件的时候,在不改代码的情况下,禁用缓存。spring对于这种机制的实现,是通过上图中没有画出的两个特殊的类来实现的: NoOpCacheManager和NoOpCache类。这两个类分别是CacheManager类Cache类的子类,表示不进行任何缓存操作。

     在ConcurrentMapCacheManager内置的缓存管理器中,可以通过配置指定一个boolean类型的allowNullValues属性,用于指定缓存中能否保存null值。因为该管理器是用于spring通过Map实现的内置缓存的管理器实现。在对应的Cache实现类ConcurrentMapCache中可以看到,它是通过ConcurrentHashMap保存所有建值对数据的。然而ConcurrentHashMap并不支持保存null值,直接在ConcurrentHashMap中put空值会抛空指针异常。然而,往缓存中保存空值有时候确实也是有必要的。比如,在从数据库查询某项数据时,因数据不存在,返回了null。这时候如果不把这个null值保存到缓存中去,那么下次再作查询时,缓存就无法命中,从而导致重复查询数据库,这就是所谓的缓存穿透。为了防止这种情况,这就要对null值做一个包装,把它包装成一个非null的而且在业务上认为是无效的对像保存到缓存里面。ConcurrentMapCacheManager中的allowNullValues就是用于指定能否缓存null,如果此值为true,将把自动把null包装成无效对像缓存起来,如果为false,那么需要开发人员自行从业务层上保证不往缓存中保存null数据。

      在上面类图中,最重要的是类就是AbstractCacheManager抽象类了,它只对CacheManager提供了一个简单实现,并开放了一些比如loadCaches()之类的抽象方法,对于这个类的具体实现,由需要集成的具体的缓存厂商来实现。

      AbstractCacheManager类除了实现CacheManager接口之外,还实现了srping框架的InitializingBean接口,这使得此类型的bean 在被spring初始化的时候,会自动调用afterPropertiesSet()方法,这个方法会调用此类的initializeCaches()的方法进行初始化。它的具体逻辑是通过用调用抽象方法loadCaches()获取它能管理的所有Cache实列列表,并遍历它,把它都添加到此实例的cacheMap属性集合中,同时把所有的name都统一加入到cacheNames集合中,以便方法getCacheNames()可以返回所有cache的名称集合。

     抽象方法loadCaches()的作用是从具体的缓存实现中加载所有它能管理的Cache(比如调用EhCache相关的api加载他所有的Cache)。

     在这个类中,最重要的方法就是getCache(String),表示通过缓存名称获取缓存实例。它的实现逻辑是,先从cacheMap集合以name作为key查找cache,如果找不到,就调用getMissingCache()方法获取。这个getMissingCache()意图为:返回loadCaches()原来没有加载到的Cache(这里有一次重新加载的机会)。当然,AbstractCacheManager这个类并没有对这个方法做特别的实现,只是简单返回了null,具体的实现类可以覆盖这个方法。

    这个类中还有其它的诸如addCache()之类的方法,就不介绍了。下面看看spring自带的ehcache实现。

4spring cache内置的ehcache支持方案:

     spring cache模块通过提供ehcache相关的几个实现类对ehcache进行支持,采用的是适配器设计模式,相关类在srping-cotext-suppoert-xxx.jar包中如下类图:

     图中可看出,最重要的两个类就是EhCacheCache和EhCacheCacheManager,分别是Cache和AbstractCacheManager的子类实现类。这两个类中,对于父类抽象的方法的实现,都是委托它类部的Ehcache及net.sf.ehcache.CacheManager通过调用ehcache相关api来完成。

    然而,因为ehcache的配置参数比较多,为了方便开发人员简洁的配置EhCacheCache和EhCacheCacheManager实例。spring提供了两个对应的工厂类: EhCacheFactoryBean和EhCacheManagerFactoryBean。开发人员只要在配置文件中配置这两个类型的bean,就可以很方便的完成与ehcache的集成 。

4、集成memcache方案提示:

     spring并没有提供对memcache的直接支持,需要我们自己实现,通过以上分析,可以想象,集成memcache的主要思路就是实现自己的CacheManager及Cache类。限于篇幅,在后面的文章中再给出具体的实现方案。

     想了解更多信息的同学们可以扫以下二维码关注我的微信公众号:

转载于:https://my.oschina.net/u/1415710/blog/853892

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通用缓存框架,spring缓存模块原理分析篇 的相关文章

  • 编程常用英语词汇(一)

    编程常用英语词汇 一 编程对英语的要求自不必说 这个技能是必须的 在平时开发和学习过程中 就算你不能完全读懂这句话的意思 如果你了解其中某个关键词的意思 也能大概猜到这句话的意思 这样的话也能对你解决问题提供很大的帮助 这就是我为什么要进行
  • Paperreading之三Simple Baselines for Human Pose Estimation

    本次paper是coco2018关键点检测项目的亚军方案 方法非常的简洁明了 但是效果很惊艳 达到了state of the art paper的标题也是写了simple baseline 整篇paper包含一个sota的姿态估计和姿态跟踪
  • setchecked方法会触发OnCheckedChangeListener

    分享一下我老师大神的人工智能教程 零基础 通俗易懂 风趣幽默 还带黄段子 希望你也加入到我们人工智能的队伍中来 https blog csdn net jiangjunshow 转载请标明出处 http blog csdn net xx32
  • 模式识别机器学习PRML考前自测

    绪论 1 请介绍一下机器学习的整体框架 机器学习和模式识别基本一个东西 模式识别是工业界的称呼而已 机器学习是人工智能下属的子领域 用来辅助在大数据时代进行数据分析与数据管理 应用于数据挖掘工作 人工智能的其他子领域包括比如NLP CV等领
  • in module XXX. File is included in 4 contexts

    问题描述 整合SSM框架时 出现以下情况 原因分析 这四个配置文件应该位于同一个ApplicationContext下 解决方案
  • linux执行python程序实时重定向输出日志【-u参数】

    执行python程序的时候 不能实时输出 总是在执行完成之后再一次性的输出到日志中 对于长时间的 希望看到中间过程的情况 不是很方便 查了下是因为缓存的问题 u 表示不启用缓存 实时输出打印信息到日志文件 如果不加 u 则会导致日志文件不会
  • 框架到底是干什么用的?

    不用刻意地在设计中体现MVC 而是在不知不觉中写出符合优秀架构的代码 这应该是所有框架的共同目的 也应该是根本目的
  • 绝大部分人都理解错了HTTP中GET与POST的区别

    GET和POST是HTTP请求的两种基本方法 要说它们的区别 接触过WEB开发的人都能说出一二 最直观的区别就是GET把参数包含在URL中 POST通过request body传递参数 你可能自己写过无数个GET和POST请求 或者已经看过
  • keras神经网络入门:1.简单神经网络实现mnist识别,识别准确率高达89%

    第一步 导入相关模块 import tensorflow as tf from tensorflow keras import layers models from tensorflow keras datasets import mnis
  • 【深度学习】万字综述:用于深度神经网络加速的Shift操作

    作者丨科技猛兽 编辑丨极市平台 导读 如何同时满足减少可学习参数的数量以及维持computation memory access比值 你需要Shift操作 本文将详细介绍shift操作的具体方法 如何剪掉冗余的Shift操作 3种用于深度神
  • 【大模型系列 06】LLaMA-7B/13B for PyTorch 昇腾迁移

    源码链接 https gitee com ascend ModelZoo PyTorch tree master PyTorch built in foundation LLaMA 13B LLaMA 7B 13B for PyTorch
  • WIN10+Darknet自建简单数据集训练YOLOv3识别特定物体

    1 在darknet master build darknet x64 新建yolo obj cfg文件 可以直接复制yolov3 cfg 然后重命名为yolo obj cfg 然后就是修改这个文件内容了 将batch 改成64 batch
  • 类别不平衡

    类别不平衡产生原因 类别不平衡 class imbalance 是指分类任务中不同类别的训练样例数目差别很大的情况 产生原因 通常分类学习算法都会假设不同类别的训练样例数目基本相同 如果不同类别的训练样例数目差别很大 则会影响学习结果 测试
  • (八)BaseTableView的简单使用

    BaseTableView的简单使用 现在来说说另一个我封装比较重的UI组件 这个组件要实现什么效果呢 当你使用继承了BaseTableView的子类tableView后 只需要在使用这个类的控制器中传递给tableView一个数据数组 并
  • Vue3回到顶部(BackTop)

    效果如下图 在线预览 APIs 参数 说明 类型 默认值 必传 bottom BackTop 距离页面底部的高度 number string 40 false right BackTop 距离页面右侧的宽度 number string 40
  • 【Three.js】第六章 Animations 动画

    06 Animations动画 介绍 我们创建了一个场景 我们在代码末尾渲染了一次 这已经是不错的进步了 但大多数时候 您会想要为您的作品制作一些动态效果动画 使用 Three js 时 动画效果类似于定格动画 您移动对象 然后进行渲染 然
  • 3、OC运算符优先级

    OC运算符有以下几种 1 算术运算符 2 赋值运算符 lt lt gt gt 3 比较运算符 gt lt gt lt 4 逻辑运算符 5 位运算符 lt lt gt gt 6 类型相关运算符 运算符优先级如下 优先级 运算符 名称或含义
  • QML(一):自定义圆角按钮的处理

    设计自己的按钮作为组件使用 1 设置相关属性 供外部组件调用时 直接更改属性改变组件样式 定义不同圆角状态下的按钮 enum Style NormalButton LowRoundCornerButton HighRoundCornerBu
  • 分页页码的前端显示的实现

    分页页码的前端显示实现 效果说明 准备做分页的时候滚回去找了一下之前写过的demo结果被自己写的丑哭了 大概长这样 主要是不符合现在一般网站的跳转习惯 自己用着都觉得不方便 所以重新写了一下新的分页的前端 顿时觉得舒服了许多 js思路 首先
  • 用python爬取网站小说,帮你们省下会费

    第一步 导库 import requests from lxml import etree 第二步 请求头 headers User Agent Mozilla 5 0 Windows NT 6 1 Win64 x64 AppleWebKi

随机推荐

  • androidstudio无法真机安装debug包 android无法安装debug包 oppo vivo

    1 adb 无法安装debug包 adb install t apk path 绝对路径 2 android stduio 连接设备 点击run 安装失败 1 在application 节点下新增android testOnly true
  • 腾讯mini项目-【指标监控服务重构】2023-07-19

    今日已办 OpenTelemetry Logs 通过日志记录 API 支持日志收集 集成现有的日志记录库和日志收集工具 Overview 日志记录 API Logging API 允许您检测应用程序并生成结构化日志 旨在与其他 teleme
  • 输入IP使用JDBC读取数据库信息并返回具体地址

    思路分析 定义连接类 连接数据库 定义properties 存放mysql信息 读取properties 注册驱动 获取连接 定义测试类 运行主程序 定义输入类 检测控制台输入 定义正则表达式 对输入的ip进行判断 判断通过则调用核心逻辑
  • c++利用libcurl获取http网页的 response headers 等信息

    参考 http www update8 com Program C 26438 html libcurl 7 32 0下载地址 http download csdn net detail chuanyu 9226175 demo下载地址 h
  • 摘要(Hash)算法总结

    数据摘要算法具有不可逆性 其主要功能有数据签名 数据完整性校验等 下面介绍常见的数据摘要算法 一 CRC8 CRC16 CRC32 CRC Cyclic Redundancy Check 循环冗余校验 算法出现时间较长 应用广泛 尤其是通讯
  • 机考[21-30]

    华为机考 021 喊7的次数重排 022 猴子爬山 023 滑动窗口最大和 024 火星文计算 025 计算面积 026 计算最大乘积 027 检查是否存在满足条件的数字组合 028 矩阵扩散 029 矩阵最大值 030 考勤信息 021
  • Vue3子组件向父组件传值

    需要用到emit 举个栗子 比如希望子组件 newHeader vue 向父组件 news vue 传labelId 子组件中先定义一个函数 用于传值 以下是写在子组件 newHeader vue 中的代码 其中 getLabelId是在子
  • unity更改帧率的方法

    介绍 unity更改帧率的方法 方法一 通过编辑器更改帧率 a 在Unity编辑器中 选择 Edit 编辑 菜单 然后选择 Project Settings 项目设置 b 在 Project Settings 中选择 Time 时间 选项卡
  • Python解释器以及PyCharm的安装

    通过CSDN上 大神写的教程 安装了PyCharm 现自己总结Python解释器安装以及PyCharm安装过程 一 Python解释器安装 解释器 英语 Interpreter 又译为直译器 是一种电脑程序能够把高级编程语言一行一行直接转译
  • python中__call()__函数和TensorFlow中call()函数的区别

    在使用python的时候 经常用到python中的特殊函数 call 函数 但是学习TensorFlow的时候 又会看到自定义模型的时候 经常用call 函数 不懂他们之间的区别 python中的 call 函数和TensorFlow中的c
  • Switch NS 通过华硕梅林路由DNSMASQ屏蔽任天堂服务器

    好吧 我是把我的古董NS机给大气层了 今天来说下如何通过dnsmasq把任天堂的服务器给屏蔽了 虽然我还没有测试过这个屏蔽是否能够避免BAN机 作者免责声明 本文以下内容不对其有效性 安全性 可靠性负责 利用本文所提出的技术内容产生的一切正
  • 【KubeEdge V1.10.0安装】Vmware Workstation17pro、CentOS 7系统、keadm教程

    因为个人原因需要使用虚拟机搭建kubeedge 之前成功装过一次 但是未记录安装步骤 后续虚拟机崩了 需要重新安装 本文仅为整理记录 安装主要参考文章链接如下 http t csdn cn sMnFa PS 吐槽一下1 7版本 cloudc
  • 接口测试(一)

    目录 1 接口的定义 2 接口的类型 3 接口测试的类型 4 web接口测试 4 1 Web 接口测试的方法 4 2 Web 接口的测试点 接口测试的准备 用工具postman进行web测试 实战一之登录接口 实战二之支付接口 实战三发送红
  • springBoot设置不同环境配置文件

    配置 根目录下新建三个环境的配置文件 application dev properies开发环境 application test properties 测试环境 application prod properties 正式环境 在appl
  • js实现防抖函数,输入框持续输入打印最终的值

    js实现防抖函数 输入框持续输入打印最终的值 防抖是闭包的实际应用之一 一 防抖函数 概念 当高频事件持续触发时 只有当一定时间内没有再次触发事件 事件才会执行一次 若一定时间内再次触发事件 则重新计时 比较类似lol中回城被打断后 再次回
  • 电脑重装系统后安全中心有个黄色感叹号如何处理

    近期有部分Win11用户在电脑的使用中 发现安全中心有感叹号 这对于强迫症来说很不舒服 而且试过清除图标缓存的脚本的方法 都不能去除 那么有没有什么有效的方法解决呢 下面我们一起来看看小编是如何操作的吧 更多电脑重装系统可参考 解决方法如下
  • curl -w用法大全

    顾名思义 write out的作用就是输出点什么 curl的 w参数用于在一次完整且成功的操作后输出指定格式的内容到标准输出 输出格式由普通字符串和任意数量的变量组成 输出变量需要按照 variable name 的格式 如果需要输出 do
  • 是什么影响了MySQL性能?

    是什么影响了MySQL性能 影响性能的一些常见因素 服务器硬件 服务器系统 数据库存储引擎 MyISAM 可以很好的利用内存 但不支持事务 表级锁 InnoDB 事务级存储引擎 完美支持行级锁以及事务ACID特性 数据库参数配置 数据库表结
  • 关于Hive中case when不准使用子查询的解决方法

    关于Hive中case when不准使用子查询的解决方法 参考文章 1 关于Hive中case when不准使用子查询的解决方法 2 https www cnblogs com harrylyx p 12236441 html 3 http
  • 通用缓存框架,spring缓存模块原理分析篇

    在设计自己的缓存框架之前 有必要了解一下spring的cache模块 在spring3 1及以后的版本中 提供了基于注解的缓存支持 但spring并没有对缓存进行具体实现 除了提供一个简单的基于Map的实现之外 本框架就是在此基础上进行扩展