Spring Cache @Cacheable:提升应用性能的利器
在构建企业级应用时,性能优化至关重要。Spring Cache 提供了一种简便而强大的方式来缓存方法调用的结果,从而减少数据库访问、提高响应速度。其中,@Cacheable 注解是 Spring Cache 的核心,本文将深入剖析 @Cacheable 注解,助你掌握缓存技术,打造更高效的应用。
一、Spring Cache 简介
Spring Cache 是 Spring 框架提供的抽象层,用于简化缓存的使用。它提供了一组注解和接口,使得开发者可以轻松地集成不同的缓存解决方案(如Ehcache、Caffeine、Redis等),而不必关心底层的具体实现细节。Spring缓存抽象层主要包括以下几个核心组件:
- 缓存管理器(CacheManager):负责管理缓存实例。
- 缓存(Cache):实际存储缓存数据的容器。
- 缓存注解:如@Cacheable、@CachePut、@CacheEvict等,用于定义缓存的行为。
二、@Cacheable 注解的作用
@Cacheable 注解用于将方法的返回值缓存起来。当调用被 @Cacheable 注解的方法时,Spring Cache 会首先检查缓存中是否存在对应 key 的缓存数据。如果缓存中存在数据,则直接从缓存中获取数据并返回,不会执行方法体。如果缓存中不存在数据,则执行方法体,并将方法返回值缓存到指定的缓存中。
三、@Cacheable 注解的属性
- cacheNames 或 value: 指定缓存的名称。可以指定一个或多个缓存名称。
@Cacheable(value = "userCache")
- key: 指定缓存的 key。可以使用 SpEL 表达式来动态生成 key。
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) { ... }
- condition: 指定缓存的条件。只有满足条件时,才会进行缓存。可以使用 SpEL 表达式来定义条件。
@Cacheable(value = "users", condition = "#id > 0")
public User getUserById(Long id) { ... }
- unless: 指定不缓存的条件。只有不满足条件时,才会进行缓存。可以使用 SpEL 表达式来定义条件。
@Cacheable(value = "users", unless = "#result == null")
public User getUserById(Long id) { ... }
- sync: 设置为 true 时, 只有一个线程可以执行方法体, 其他线程会被阻塞直到方法执行完成。 用于解决缓存击穿问题。
@Cacheable(value = "users", sync = true)
public User getUserById(Long id) { ... }
- keyGenerator: 指定 KeyGenerator 的 Bean 名称。用于自定义 key 的生成策略。
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.util.Arrays;
@Configuration
public class MyCacheConfig {@Bean("myKeyGenerator")public KeyGenerator keyGenerator(){return new KeyGenerator(){@Overridepublic Object generate(Object target, Method method, Object... params) {return method.getName()+"["+ Arrays.asList(params).toString()+"]";}};}
}
@Cacheable(value="emp", keyGenerator = "myKeyGenerator")
- cacheManager: 指定 CacheManager 的 Bean 名称。用于指定缓存管理器。
@Configuration
public class CustomCacheManager implements CacheManager {// 实现CacheManager接口的方法,例如getCache, getCacheNames等
}
@Cacheable(value = "someCache", cacheManager = "customCacheManager")
四、简单示例
4.1 启用缓存
在 Spring Boot 启动类上添加 @EnableCaching 注解,启用缓存功能。
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@EnableCaching // 启用缓存
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}
4.2 使用 @Cacheable 注解
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class ProductService {@Cacheable(cacheNames = "products", key = "#id")public Product getProductById(Long id) {System.out.println("Executing getProductById method..."); // 模拟从数据库查询// 从数据库查询 ProductProduct product = new Product(id, "Product Name " + id);return product;}
}
在这个示例中,getProductById 方法会被缓存, 缓存名称为 products, 缓存 Key 为 id。 当第一次调用 getProductById 方法时,会执行方法体,并将返回的 Product 对象缓存到 products 缓存中。 当后续再次调用 getProductById 方法时,会直接从缓存中获取数据,而不会执行方法体,从而提高了应用的性能。
4.3 定义缓存配置
可以在 application.properties 或 application.yml 文件中定义缓存配置。
spring.cache.type= caffeine # 使用 Caffeine 缓存
spring.cache.caffeine.spec=maximumSize=1000,expireAfterAccess=60s # 配置 Caffeine 缓存的参数
五、缓存更新与失效
5.1 使用 @CachePut 更新缓存
@CachePut 注解用于在更新数据后同步更新缓存。
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@CachePut(value = "users", key = "#user.id")public User updateUser(User user) {userRepository.save(user);return user;}
}
5.2 使用 @CacheEvict 清除缓存
@CacheEvict 注解用于清除缓存中的数据。
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@CacheEvict(value = "users", key = "#id")public void deleteUser(Long id) {userRepository.deleteById(id);}
}
5.3 全部清除缓存
有时我们需要清除整个缓存,可以使用 allEntries 属性。
@CacheEvict(value = "users", allEntries = true)
public void clearUserCache() {// 清除所有用户缓存
}
六、缓存管理器配置
6.1 使用 Caffeine 作为缓存管理器
Caffeine 是一个高性能的本地缓存库,适用于单机应用。
spring:cache:type: caffeinecaffeine:spec: maximumSize=500,expireAfterAccess=60s
6.2 使用 Redis 作为缓存管理器
Redis 是一个流行的分布式缓存系统,适用于集群环境。
spring:cache:type: redisredis:host: localhostport: 6379
6.3 自定义缓存管理器
有时我们需要自定义缓存管理器,可以通过实现 CacheManager 接口来完成
@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic CacheManager cacheManager() {SimpleCacheManager cacheManager = new SimpleCacheManager();List<Cache> caches = new ArrayList<>();caches.add(new ConcurrentMapCache("users"));caches.add(new ConcurrentMapCache("configs"));cacheManager.setCaches(caches);return cacheManager;}
}
七、最佳实践与注意事项
7.1 合理选择缓存策略
- 本地缓存 vs 分布式缓存:根据应用的部署方式选择合适的缓存策略。单机应用可以选择本地缓存(如Caffeine),集群应用应选择分布式缓存(如Redis)。
- 缓存过期时间:合理设置缓存的过期时间,避免缓存数据过期导致的脏读问题。
7.2 缓存一致性
- 避免缓存穿透(缓存和数据库中都没有的数据,可用户还是源源不断的发起请求,导致每次请求都会到数据库,从而压垮数据库): 可以使用布隆过滤器或者缓存空对象来避免缓存穿透。
- 避免缓存击穿(Redis中一个热点key在失效的同时,大量的请求过来,从而会全部到达数据库,压垮数据库): 可以使用互斥锁或者将缓存过期时间设置为随机值来避免缓存击穿。
- 注意缓存雪崩(Redis中缓存的数据大面积同时失效,或者Redis宕机,从而会导致大量请求直接到数据库,压垮数据库): 可以使用多级缓存来避免缓存雪崩,建议采用随机过期时间或分批失效策略。
7.3 缓存监控
- 监控缓存命中率:定期监控缓存的命中率,评估缓存的效果。
- 日志记录:记录缓存操作的日志,便于排查问题。
相关文章:
Spring Cache @Cacheable:提升应用性能的利器
在构建企业级应用时,性能优化至关重要。Spring Cache 提供了一种简便而强大的方式来缓存方法调用的结果,从而减少数据库访问、提高响应速度。其中,Cacheable 注解是 Spring Cache 的核心,本文将深入剖析 Cacheable 注解࿰…...
css块级元素和行内元素区别
在CSS中,元素可以分为两大类:块级元素(Block-level elements)和行内元素(Inline elements)。这两种元素在网页布局中起着不同的作用,主要体现在它们的显示方式、尺寸控制、以及与其他元素的交互…...
AWTK fscript 中的 TCP/UDP 客户端扩展函数
fscript 是 AWTK 内置的脚本引擎,开发者可以在 UI XML 文件中直接嵌入 fscript 脚本,提高开发效率。本文介绍一下 fscript 中的 TCP/UDP 客户端扩展函数。 1.iostream_tcp_create 创建 TCP 客户端输入输出流对象。 原型 iostream_tcp_create(host, por…...
[免费]Springboot+Vue医疗(医院)挂号管理系统【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的SpringbootVue医疗(医院)挂号管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue医疗(医院)挂号管理系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 在如今社会上,关于信息上…...
计算机毕业设计PySpark+hive招聘推荐系统 职位用户画像推荐系统 招聘数据分析 招聘爬虫 数据仓库 Django Vue.js Hadoop
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Jmeter+Influxdb+Grafana平台监控性能测试过程
一、Jmeter自带插件监控 下载地址:https://jmeter-plugins.org/install/Install/ 安装:下载后文件为jmeter-plugins-manager-1.3.jar,将其放入jmeter安装目录下的lib/ext目录,然后重启jmeter,即可。 启动Jmeter&…...
fatal: unable to access ‘https://github.com/xxx/‘: SSL peer certificat
从github上clone代码时报错 F:\Projects>git clone https://github.com/xxx into xxx... fatal: unable to access https://github.com/xxx/: SSL peer certificate or SSH remote key was not OK **可能的原因****解决方法****1. 检查系统时间****2. 禁用 SSL 验证…...
Prompt通用技巧
Prompt 的典型构成 角色:给 AI定义一个最匹配任务的角色,比如:「你是一位软件工程师」「你是一位小学老师」指示:对任务进行描述上下文: 给出与任务相关的其它背景信息(尤其在多轮交互中)。例子 : 必要时给出举例,学术中称为 one-shot learning,few-sho…...
ROACH
End-to-End Urban Driving by Imitating a Reinforcement Learning Coach CARLA-Roach ICCV‘21论文:模仿一个强化学习教练的端到端城市驾驶 文章目录 Roach输入BEV语义分割图像测量向量 Roach输出训练策略网络价值网络 具体实现由 Roach 监督的模仿学习(…...
机械臂运动学笔记(一):正向运动学
正向运动学指的是通过相邻关节间的转动和移动坐标,将末端的坐标计算出来。 反向运动学指的是已知机械臂末端的坐标,反算每个关节可能的转动和移动参数。 参考资料:4.机械臂几何法与DH表示法_哔哩哔哩_bilibili 一.任意连杆连接的变量定义&a…...
【DuodooBMS】给PDF附件加“受控”水印的完整Python实现
给PDF附件加“受控”水印的完整Python实现 功能需求 在实际工作中,许多文件需要添加水印以标识其状态,例如“受控”“机密”等。对于PDF文件,添加水印不仅可以增强文件的可识别性,还可以防止未经授权的使用。本代码的功能需求是…...
GitCode 助力 Dora SSR:开启游戏开发新征程
项目仓库(点击阅读原文链接可直达) https://gitcode.com/ippclub/Dora-SSR 跨越技术藩篱,构建游戏开发乐园 Dora SSR 是一款致力于打破游戏开发技术壁垒的开源游戏引擎。其诞生源于开发者对简化跨平台游戏开发环境搭建的强烈渴望࿰…...
Mediamtx+Python读取webrtc流
一、功能思路: 1、我采用ffmpeg -re -stream_loop -1 -i xcc.mp4 -c:v libx264 -profile:v baseline -x264opts "bframes0:repeat_headers1" -b:v 1500k -preset fast -f flv rtmp://127.0.0.1:1835/stream/111推流到mediamtx的rtmp上 2、通过mediamtx自…...
每日一题——矩阵最长递增路径
矩阵最长递增路径问题 题目描述数据范围:进阶要求:示例示例 1示例 2 题解思路算法步骤:代码实现代码解释复杂度分析总结 题目描述 给定一个 n 行 m 列的矩阵 matrix,矩阵内所有数均为非负整数。你需要在矩阵中找到一条最长路径&a…...
【CLIP系列】4:目标检测(ViLD、GLIP)
目录 1 ViLD2 GLIP2.1 前言2.2 损失计算2.3 模型框架 1 ViLD OPEN-VOCABULARY OBJECT DETECTION VIA VISION AND LANGUAGE KNOWLEDGE DISTILLATION 从标题就能看出来,作者是把CLIP模型当成一个Teacher,去蒸馏他自己的网络,从而能Zero Shot去…...
Cesium for Unity Linux版本
Cesium for Unity 直装不支持Linux 参照官方开发流程一些操作命令issues 宝藏最后运行图 参照官方开发流程 https://github.com/CesiumGS/cesium-unity/blob/main/Documentation~/developer-setup.md 系统已经安装过dotnet和cmake xuefeixuefei:~$ dotnet --version 9.0.102 …...
Spring Boot过滤器链:从入门到精通
文章目录 一、过滤器链是什么?二、为什么需要过滤器链?三、Spring Boot中的过滤器链是如何工作的?(一)过滤器的生命周期(二)过滤器链的执行流程 四、如何在Spring Boot中定义自己的过滤器&#…...
关于 IoT DC3 中驱动(Driver)的理解
在开源IoT DC3物联网系统中,驱动(Driver)扮演着至关重要的角色,它充当了软件系统与物理设备之间的桥梁。驱动的主要功能是依据特定的通信协议连接到设备,并根据设备模板中配置的位号信息进行数据采集和指令控制。不同的…...
微信小程序地图标记点,安卓手机一次性渲染不出来的问题
问题描述: 如果微信小程序端,渲染的标记物太多,安卓手机存在标记物不显示的问题,原因初步判断是地图还没有渲染完,标记物数据已经加载完了,导致没有在地图上显示。 解决办法: 使用map组件的b…...
一维差分与二维差分
差分(Difference)是一种与前缀和密切相关的技术,主要用于高效处理区间更新操作。差分数组的核心思想是通过记录相邻元素的差值来表示原数组的变化,从而将区间更新操作的时间复杂度从 O(n) 优化到 O(1)。下面详细讲解一维差分和二维…...
EasyRTC嵌入式WebRTC视频通话SDK支持Web浏览器、Linux、ARM、Android、iOS
随着互联网技术的飞速发展,实时通信(RTC)已经成为现代应用中不可或缺的一部分。无论是视频会议、在线教育、远程医疗,还是社交娱乐,实时通信技术都在其中扮演着重要角色。 然而,WebRTC技术在PC和移动端的支…...
数据库脚本MySQL8转MySQL5
由于生产服务器版本上部署的是MySQL5,而开发手里的脚本代码是MySQL8。所以只能降版本了… 升级版本与降级版本脚本转换逻辑一样 MySQL5与MySQL8版本SQL脚本区别 大多数无需调整、主要是字符集与排序规则 MySQL5与MySQL8版本SQL字符集与排序规则 主要操作&…...
【PGCCC】commit_delay 对性能的提升:PostgreSQL 基准测试
通过禁用参数可以来调整事务工作负载synchronous_commit。该措施有惊人效果。但在操作系统崩溃期间丢失已提交事务的可能性使其成为许多应用程序无法启动的因素。因此我决定写下来。 WAL 刷新是事务数据库工作负载的瓶颈 为了确保已提交的事务不会丢失,PostgreSQL…...
AI大模型随机初始化权重并打印网络结构方法(以Deepseekv3为例,单机可跑)
背景 当前大模型的权重加载和调用,主要是通过在HuggingFace官网下载并使用transformer的库来加以实现;其中大模型的权重文件较大(部分>100GB),若只是快速研究网络结构和数据流变化,则无需下载权重。本文…...
字符串解码——巧妙使用递归解题
题目描述 给定一个经过编码的字符串,返回它解码后的字符串。编码规则为 k[encoded_string],表示方括号内部的 encoded_string 重复 k 次。其中 k 是正整数,输入字符串确保符合格式要求且无额外空格。 示例: 复制 示例 1&#…...
Flask Web开发的重要概念和示例
一口气列举Flask Web应用的所有概念和示例 Flask Web 应用基本框架 路由(Routing) 模版(Template) request 对象 JSON 数据处理 redirect 示例 文件上传示例 文件下载示例 Session 示例 Cookie操作 Flask Web 应用基本框架 这是一个 最基础的 Flask Web 应用,…...
51-ArrayList
51-ArrayList Collection 类型介绍 仓颉中常用的几种基础 Collection 类型,包含 Array、ArrayList、HashSet、HashMap。 可以在不同的场景中选择适合对应业务的类型: Array:如果不需要增加和删除元素,但需要修改元素ÿ…...
Ollama+WebUI+DeepSeek部署自己的本地大模型
前言 使用AI几乎成为互联网工作者必备技能了,DeepSeek的出现把AI再次推向高潮,在本文中,我们将带领大家借助 Ollama、WebUI 和 deepseek 这三个工具,成功搭建属于自己的本地大模型环境。Ollama 作为一款轻量级的大模型运行工具&a…...
(篇六)基于PyDracula搭建一个深度学习的软件之新版本ultralytics-8.3.28调试
ultralytics-8.3.28版本debug记录 1传入文件 代码太多不粘贴在这里了,完整代码写在了篇三 def open_src_file(self):config_file config/fold.jsonconfig json.load(open(config_file, r, encodingutf-8))open_fold config[open_fold]if not os.path.exists(op…...
NLP Word Embeddings
Word representation One-hot形式 在上一周介绍RNN类模型时,使用了One-hot向量来表示单词的方式。它的缺点是将每个单词视为独立的,算法很难学习到单词之间的关系。 比如下面的例子,即使语言模型已经知道orange juice是常用组合词…...
