news 2026/9/3 7:28:34

Spring Cache 全景指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Cache 全景指南

一、 核心定义与作用

Spring Cache不是一个具体的缓存实现(它不是 Redis,也不是 EhCache),而是一套缓存抽象层(Cache Abstraction)

1. 核心定位

它类似于 JDBC 之于数据库。

  • JDBC定义了标准接口,你可以底层换 MySQL 或 Oracle。
  • Spring Cache定义了标准注解 (@Cacheable),你可以底层换 Redis、Caffeine 或 ConcurrentMap,而业务代码不需要修改一行

2. 核心价值

  1. 代码解耦:将业务逻辑(Service)与缓存技术细节分离。
  2. 消除样板代码:通过 AOP(面向切面编程)管理缓存,不再需要手动编写redisTemplate.get/set
  3. 统一标准:无论底层用什么,上层注解用法一致。

二、 核心注解体系

Spring Cache 主要通过以下 5 个注解控制缓存行为:

注解核心作用执行逻辑 (通俗版)适用场景
@EnableCaching总开关开启 Spring 的缓存代理功能。启动类 / 配置类
@Cacheable查/存1. 先查缓存,有则直接返回。2. 无则执行方法,并将结果存入缓存。查询(Get)
@CachePut改/存始终执行方法,并将返回值强制更新到缓存中。新增/修改(Save/Update)
@CacheEvict执行方法(前或后),从缓存中删除指定数据。删除(Delete)
@Caching组合在一个方法上叠加多个操作(如删 A 缓存同时删 B 缓存)。复杂联动场景

通用关键参数

所有注解(除开关外)都支持以下参数:

  • value/cacheNames:缓存名称(对应 Redis 的 Key 前缀)。
  • key:缓存 Key,支持SpEL 表达式(如#id,#user.name)。
  • condition事前判断。满足条件才处理缓存(例如:#id > 10)。
  • unless事后判断。满足条件缓存(例如:#result == null)。

三、 实践案例详解

以下模拟一个用户系统,展示如何结合 Redis 使用。

1. 查询缓存(@Cacheable)

需求:查询用户,缓存有直接返回,无则查库并回填。同时防止缓存null值。

@ServicepublicclassUserService{@AutowiredprivateUserMapperuserMapper;/** * value = "users": 对应 Redis 前缀 "users::" (或自定义的 "users:") * key = "#id": 取参数 id 作为后缀 * unless = "#result == null": 如果查不到数据,不要把 null 存进 Redis */@Cacheable(value="users",key="#id",unless="#result == null")publicUsergetUserById(Longid){System.out.println("--- 走数据库查询 ---");returnuserMapper.selectById(id);}}

2. 保持一致性(@CacheEvict 推荐)

需求:修改用户信息后,清理缓存,保证下次查询获取最新数据。

  • 最佳实践:优先使用删除模式 (Evict)而非更新模式 (Put),避免并发写导致脏数据。
/** * 更新完成后,删除对应的缓存 Key * key 必须与查询时的 key 生成规则一致 */@CacheEvict(value="users",key="#user.id")publicvoidupdateUser(Useruser){userMapper.updateById(user);}/** * 场景:删除用户,或者清空整个缓存桶 * allEntries = true: 删除 users 下的所有 key */@CacheEvict(value="users",allEntries=true)publicvoidclearAllCache(){System.out.println("清空用户缓存");}

3. 复杂 Key 生成 (SpEL)

需求:根据多个参数组合生成 Key。

/** * 假设 type=VIP, page=1 * Redis Key: user_list:VIP_1 */@Cacheable(value="user_list",key="#type + '_' + #page")publicList<User>getUsersByType(Stringtype,intpage){returnuserMapper.selectByType(type,page);}

四、 避坑指南:内部调用失效 (Self-Invocation)

这是 Spring Cache 最经典的“大坑”。

1. 现象描述

在同一个 Service 类内部,方法 A 调用带@Cacheable的方法 B,方法 B 的缓存注解失效,每次都会执行 SQL。

2. 原理图解

Spring Cache 基于代理模式 (Proxy Pattern)

  • 外部调用:Controller -> Proxy对象 -> 拦截处理缓存 -> 目标对象。
  • 内部调用this.method()是目标对象自己调用自己,绕过了 Proxy 对象,所以 AOP 切面无法执行。

3. 代码示例与修复

❌ 错误写法 (失效)
@ServicepublicclassUserService{// 方法 A:批量查询publicList<User>getUsersByIds(List<Long>ids){List<User>users=newArrayList<>();for(Longid:ids){// 【失效原因】:这里等同于 this.getUserById(id)// 直接调用了类内部的方法,没有经过 Spring 的代理类users.add(getUserById(id));}returnusers;}// 方法 B:单查(注解在此)@Cacheable(value="users",key="#id")publicUsergetUserById(Longid){System.out.println("查询数据库...");returnnewUser(id,"name");}}
✅ 修复方案 1:自我注入 (简单有效)

通过注入自身(代理对象)来调用,强行经过代理层。

@ServicepublicclassUserService{// 1. 注入自己 (加上 @Lazy 防止循环依赖报错)@Autowired@LazyprivateUserServiceself;publicList<User>getUsersByIds(List<Long>ids){List<User>users=newArrayList<>();for(Longid:ids){// 2. 【修复】:使用代理对象 self 调用,而不是 this// 流程:self -> Proxy -> 检查缓存 -> Targetusers.add(self.getUserById(id));}returnusers;}@Cacheable(value="users",key="#id")publicUsergetUserById(Longid){// ...}}
✅ 修复方案 2:服务拆分 (架构更优)

将缓存方法抽离到另一个 Service 中,符合单一职责原则。

// 新服务:专门负责缓存操作@ServicepublicclassUserCacheService{@Cacheable(value="users",key="#id")publicUsergetUserById(Longid){...}}// 原服务:注入上面的服务@ServicepublicclassUserService{@AutowiredprivateUserCacheServiceuserCacheService;publicList<User>getUsersByIds(List<Long>ids){// 外部调用,缓存必然生效returnids.stream().map(userCacheService::getUserById).collect(Collectors.toList());}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 23:28:05

NCM解密工具:打破音乐格式壁垒,让加密音频重获新生

你是否曾经下载了心爱的网易云音乐&#xff0c;却因为NCM加密格式而无法在其他设备上播放&#xff1f;这种平台限制让音乐体验大打折扣。别担心&#xff0c;NCM解密工具就是你的技术伙伴&#xff0c;它能轻松解除NCM文件的加密束缚&#xff0c;让音乐真正属于你。 【免费下载链…

作者头像 李华
网站建设 2026/9/2 23:28:45

PyTorch与C++集成:通过TorchScript部署生产环境

PyTorch与C集成&#xff1a;通过TorchScript部署生产环境 在构建高并发、低延迟的AI服务时&#xff0c;一个常见的困境浮出水面&#xff1a;研究阶段用PyTorch写模型非常灵活高效&#xff0c;但一旦进入线上部署&#xff0c;Python的运行时开销和GIL限制就成了性能瓶颈。更不用…

作者头像 李华
网站建设 2026/9/3 0:48:06

PyTorch模型部署Kubernetes集群管理GPU资源

PyTorch模型部署Kubernetes集群管理GPU资源 在当今AI驱动的业务场景中&#xff0c;企业不再满足于“模型能跑”&#xff0c;而是追求“高效、稳定、可扩展”的生产级部署。一个训练好的PyTorch模型&#xff0c;若无法快速上线、弹性伸缩并充分利用昂贵的GPU资源&#xff0c;其…

作者头像 李华
网站建设 2026/9/3 1:23:53

显卡优化神器NVIDIA Profile Inspector:解锁隐藏性能的终极指南

显卡优化神器NVIDIA Profile Inspector&#xff1a;解锁隐藏性能的终极指南 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector 还在为游戏卡顿、画面撕裂而烦恼吗&#xff1f;想要充分释放显卡潜能却不知从…

作者头像 李华
网站建设 2026/9/2 22:46:41

3分钟掌握UML绘图:零安装在线编辑器的终极指南

3分钟掌握UML绘图&#xff1a;零安装在线编辑器的终极指南 【免费下载链接】plantuml-editor PlantUML online demo client 项目地址: https://gitcode.com/gh_mirrors/pl/plantuml-editor 还在为复杂的UML绘图软件头疼吗&#xff1f;这款免费的在线UML绘图工具让你彻底…

作者头像 李华
网站建设 2026/9/2 22:46:20

PyTorch模型预测接口封装为gRPC服务(GPU后端)

PyTorch模型预测接口封装为gRPC服务&#xff08;GPU后端&#xff09; 在当前AI系统日益走向生产化的背景下&#xff0c;如何将训练好的深度学习模型高效、稳定地部署为可调用的服务&#xff0c;已成为工程落地的关键一环。尤其是在图像识别、语音处理等实时性要求高的场景中&am…

作者头像 李华