Spring Boot 缓存注解详解:@Cacheable、@CachePut、@CacheEvict(超详细实战版)

news2025/6/6 20:48:05

💡 前言

在高并发、高性能的系统开发中,缓存是提升接口响应速度和降低数据库压力的重要手段。Spring Boot 提供了强大的缓存抽象层 —— spring-context-support,并结合 JSR-107 标准,提供了多个缓存注解,如:

  • @Cacheable
  • @CachePut
  • @CacheEvict

这些注解可以让我们在不侵入业务逻辑的前提下,轻松实现方法级别的缓存管理。

本文将带你从零开始掌握 Spring Boot 中常用的缓存注解,并通过多个实际案例演示其强大功能,包括:

  • 查询缓存优化
  • 数据更新同步
  • 删除缓存策略
  • 多级缓存架构设计
  • Redis 实战整合
  • 缓存穿透、击穿、雪崩的解决方案

无论你是初学者,还是有一定经验的开发者,这篇文章都能让你快速上手 Spring Boot 缓存机制!


📦 一、Spring Boot 缓存核心机制概述

✅ 缓存抽象原理

Spring 的缓存机制基于 AOP 实现,本质上是在方法执行前后插入缓存逻辑:

注解功能
@Cacheable先查缓存,有则返回;没有则执行方法并将结果缓存
@CachePut执行方法后更新缓存(常用于新增或更新操作)
@CacheEvict清除缓存(常用于删除或刷新缓存)

📌 注意:

  • 这些注解只能用在 public 方法上;
  • 只有当调用方通过 Spring 代理调用该方法时才生效(即不能在同一个类中直接调用带注解的方法);
  • 需要启用缓存功能:@EnableCaching

🛠️ 二、快速入门:开启缓存支持

Step 1:添加依赖(以 Redis 为例)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

也可以使用本地缓存如 CaffeineEhcache

Step 2:启用缓存

@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

🔁 三、三大缓存注解详解与实战

✅ 1. @Cacheable:查询缓存,避免重复计算

场景说明:

当你有一个查询接口,数据变更频率低,但查询频繁,就可以使用 @Cacheable 来缓存结果。

示例代码:
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Cacheable(value = "users", key = "#userId")
    public User getUserById(Long userId) {
        System.out.println("【真实查询】用户ID:" + userId);
        return userRepository.findById(userId).orElse(null);
    }
}
参数说明:
  • value / cacheNames:指定缓存名称,可理解为缓存区域;
  • key:缓存键,默认使用参数生成;
  • unless:条件判断,例如 unless = "#result == null" 表示结果为空时不缓存;
  • condition:根据条件决定是否缓存,例如 condition = "#userId > 0"

✅ 2. @CachePut:更新缓存,保持一致性

场景说明:

当你对数据进行了更新或新增操作,需要同步更新缓存,保证下次查询拿到的是最新数据。

示例代码:
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
    return userRepository.save(user);
}

📌 注意:

  • @CachePut 总是会执行方法体,并将结果放入缓存;
  • 常用于新增、修改等写操作。

✅ 3. @CacheEvict:清除缓存,强制刷新

场景说明:

当你删除某个数据或批量清空缓存时,就需要使用 @CacheEvict

示例代码:
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(Long userId) {
    userRepository.deleteById(userId);
}
清空整个缓存区:
@CacheEvict(value = "users", allEntries = true)
public void clearAllUsers() {
    // 删除所有用户逻辑
}

📌 参数说明:

  • allEntries = true:清空该缓存区域下的所有键;
  • beforeInvocation = true:是否在方法执行前清空缓存(默认 false,在方法执行后)。

🎯 四、进阶技巧与最佳实践

✅ 1. 自定义缓存 Key 生成策略

可以通过实现 KeyGenerator 接口自定义缓存 key 的生成方式:

@Bean
public KeyGenerator customKeyGenerator() {
    return (target, method, params) -> {
        StringBuilder sb = new StringBuilder();
        sb.append(target.getClass().getSimpleName());
        sb.append(method.getName());
        for (Object obj : params) {
            sb.append(obj.toString());
        }
        return sb.toString();
    };
}

然后在注解中使用:

@Cacheable(value = "users", keyGenerator = "customKeyGenerator")

✅ 2. 设置缓存过期时间(以 Redis 为例)

application.yml 中配置:

spring:
  redis:
    timeout: 60s
  cache:
    redis:
      time-to-live: 3600000 # 1小时

✅ 3. 多级缓存策略(Redis + Caffeine)

为了进一步提升性能,可以采用“本地缓存 + 分布式缓存”的组合方案:

  • 使用 Caffeine 作为本地缓存,减少网络开销;
  • 使用 Redis 作为分布式缓存,确保多节点间数据一致。
示例配置:
@Configuration
public class CacheConfig {

    @Bean
    public CacheManager caffeineCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("localUsers");
        cacheManager.setCacheBuilder(Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES));
        return cacheManager;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

✅ 4. 缓存穿透、击穿、雪崩解决方案

问题解决方案
缓存穿透缓存空值、使用布隆过滤器
缓存击穿加互斥锁、设置永不过期
缓存雪崩设置不同过期时间、集群缓存
示例:防止缓存穿透
@Cacheable(value = "products", key = "#productId", unless = "#result == null")
public Product getProductById(Long productId) {
    Product product = productRepository.findById(productId).orElse(null);
    if (product == null) {
        // 缓存空值,防止缓存穿透
        redisService.setWithExpire("product:" + productId, "", 60, TimeUnit.SECONDS);
    }
    return product;
}

🧩 五、综合案例:商品信息缓存实战

背景:

我们有一个商品服务,包含如下功能:

  • 查询商品详情(缓存)
  • 更新商品信息(更新缓存)
  • 删除商品(清除缓存)

代码示例:

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Cacheable(value = "products", key = "#productId")
    public Product getProductById(Long productId) {
        return productRepository.findById(productId).orElse(null);
    }

    @CachePut(value = "products", key = "#product.id")
    public Product updateProduct(Product product) {
        return productRepository.save(product);
    }

    @CacheEvict(value = "products", key = "#productId")
    public void deleteProduct(Long productId) {
        productRepository.deleteById(productId);
    }
}

📘 六、常见问题与解决方案

问题原因解决办法
注解不生效没加 @EnableCaching添加注解
同一类内调用失效AOP 代理失效将方法抽离到另一个 Bean 中
缓存未更新没使用 @CachePut@CacheEvict加上对应注解
key 冲突key 重复导致覆盖使用更细粒度的 key 策略
不支持序列化缓存对象未实现 Serializable使用 JSON 序列化或实现接口

🧭 七、总结对比表

注解作用是否执行方法适用场景
@Cacheable查询缓存有缓存则跳过方法查询频繁、更新少的数据
@CachePut更新缓存总是执行方法新增/修改操作
@CacheEvict删除缓存总是执行方法删除或刷新缓存

🎁 八、结语

Spring Boot 的缓存注解为我们提供了一种声明式的缓存管理方式,极大地提升了开发效率和系统性能。无论是构建企业级后台系统,还是搭建高并发 API 平台,都应该合理使用缓存来优化系统表现。

通过本文的学习,你已经掌握了:

  • 如何使用 @Cacheable@CachePut@CacheEvict
  • 如何结合 Redis 或本地缓存进行实际应用;
  • 如何避免缓存穿透、雪崩、击穿等问题;
  • 如何提高系统的响应速度和稳定性。

🎯 点赞、收藏、转发本文,让更多开发者受益!

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

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

相关文章

【设计模式-4.8】行为型——中介者模式

说明&#xff1a;本文介绍行为型设计模式之一的中介者模式 定义 中介者模式&#xff08;Mediator Pattern&#xff09;又叫作调节者模式或调停者模式。用一个中介对象封装一系列对象交互&#xff0c;中介者使各对象不需要显式地互相作用&#xff0c;从而使其耦合松散&#xf…

行列式的性质

1 行列式使用如下性质定义 1&#xff09;单位矩阵行列式值为 1&#xff0c; &#xff0c;对于任意单位矩阵均成立&#xff1b; 2&#xff09;当矩阵交换一行后&#xff0c;行列式值改变符号&#xff0c;如置换矩阵的行列式值为 &#xff08;根据行交换次数决定&#xff09;&…

联软NSPM自动化策略管理 助力上交所加速国产化替代提升运维效率

在金融行业核心基础设施国产化浪潮与网络安全强监管的双重背景下&#xff0c;上海证券交易所&#xff08;以下简称“上交所”&#xff09;积极拥抱变革&#xff0c;携手长期合作伙伴联软科技&#xff0c;成功部署了联软安全策略管理系统&#xff08;NSPM&#xff09;。该项目不…

Flask + ECharts+MYSQL物联网数字化大屏

基于Flask+ECharts的物联网数字化大屏系统,包含中国地图实时数据更新功能。这个系统模拟了物联网设备在全国范围内的分布和运行状况,并实时更新数据。 一、系统架构设计 技术栈 后端:Flask(轻量级路由+API支持) 前端:ECharts(地图+动态图表)、WebSocket(实时更新)…

数据库系统概论(十六)数据库安全性(安全标准,控制,视图机制,审计与数据加密)

数据库系统概论&#xff08;十六&#xff09;数据库安全性 前言一、数据库安全性1. 什么是数据库安全性&#xff1f;2. 为何会存在安全问题&#xff1f; 二、安全标准的发展1. 早期的“开拓者”&#xff1a;TCSEC标准2. 走向国际统一&#xff1a;CC标准3. TCSEC和CC标准有什么不…

vue3从入门到精通(基础+进阶+案例)

Vue是什么&#xff1f; 渐进式JavaScript框架&#xff0c;易学易用&#xff0c;性能出色&#xff0c;适用场景丰富的Web前端框架 为什么要学习Vue Vue是目前前端最火的框架之一 Vue是目前企业技术栈中要求的知识点 Vue可以提升开发体验 。。。 Vue简介 Vue(发音为/vju/,…

【Linux 学习计划】-- 系统中进程是如何调度的(内核进程调度队列)

目录 回顾进程优先级与进程调度的引入 内核runqueue图例 关于queue[140]前100个位置 | 实时进程与分时进程 遍历需要调度的进程与bitmap的引入 active、expired指针 结语 回顾进程优先级与进程调度的引入 在我们之前的学习中&#xff0c;我们是有学习过进程优先级这个概…

C#、VB.net——如何设置窗体应用程序的外边框不可拉伸

以Visual studio 2015为例&#xff0c;具体操作如下&#xff1a; 1、将窗体的“FormBorderStyle”属性值修改为“FixedSingle”&#xff1a; 2、点击“格式”——“锁定控件”&#xff1a; 这样生成的程序边框即可固定住&#xff0c;无法拉伸。

基于SpringBoot的房屋租赁系统的设计与实现(thymeleaf+MySQL)

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

Spring Boot统一功能处理深度解析

第一章&#xff1a;为什么需要统一功能处理&#xff1f; 想象你正在开发一个电商系统&#xff0c;包含用户管理、商品管理、订单管理等模块。每个模块都需要&#xff1a; 用户身份验证操作日志记录异常统一处理数据格式标准化 如果每个模块都单独实现这些功能&#xff1a; …

世事无常,比较复杂,人可以简单一点

2025年6月5日日&#xff0c;17~28℃&#xff0c;一般 待办&#xff1a; 宣讲会 职称材料的最后检查 职称材料有错误&#xff0c;需要修改 期末考试试题启用 教学技能大赛PPT 遇见&#xff1a;部门宣传泰国博士项目、硕士项目、本科项目。 感受或反思&#xff1a;东南亚博士…

数据结构哈希表总结

349. 两个数组的交集 力扣题目链接(opens new window) 题意&#xff1a;给定两个数组&#xff0c;编写一个函数来计算它们的交集。 说明&#xff1a; 输出结果中的每个元素一定是唯一的。 我们可以不考虑输出结果的顺序。 public int[] intersection(int[] nums1, int[] num…

IDEA 开发PHP配置调试插件XDebug

1、安装PHP环境 为了方便&#xff0c;使用的PhpStudy。 安装路径&#xff1a;D:\resources\phpstudy_pro\Extensions\php\php7.3.4nts 2、下载Xdebug Xdebug: Downloads 选择对应的版本下载&#xff0c;本次使用的是7.3。 3、配置Xdebug 在php.ini中添加Xdebug配置。 D…

奇异值分解(SVD):线性代数在AI大模型中的核心工具

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#, Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开…

MySQL——视图 用户管理 语言访问

目录 视图 用户管理 数据库权限 访问 准备工作 使用函数 mysql界面级工具 连接池 视图 这里的视图与事务中的读视图是两个不同的概念&#xff1a;视图是一个虚拟表&#xff0c;其内容由查询定义。同真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。视图的…

二、Sqoop 详细安装部署教程

作者&#xff1a;IvanCodes 日期&#xff1a;2025年6月2日 专栏&#xff1a;Sqoop教程 Apache Sqoop 是一个强大的工具&#xff0c;用于在 Hadoop (HDFS, Hive, HBase) 与关系型数据库 (如 MySQL, PostgreSQL, Oracle) 之间高效传输数据。本教程将详细指导您如何根据官方网站截…

【C语言预处理详解(下)】--#和##运算符,命名约定,命令行定义 ,#undef,条件编译,头文件的包含,嵌套文件包含,其他预处理指令

目录 五.#和##运算符 5.1--#运算符 5.2--##运算符 六.命名约定&#xff0c;#undef&#xff0c;命令行定义 6.1--命名约定 6.2--#undef 6.3--命名行定义 七.条件编译 常见的条件编译指令&#xff1a; 1.普通的条件编译&#xff1a; 2.多个分支的条件编译(可以利用条…

03.搭建K8S集群

K8S集群搭建的方式 目前主流的搭建k8s集群的方式有kubeadm、minikube、二进制包三种方式&#xff1a; kubeadm&#xff08;本案例搭建方式&#xff09; 是一个工具&#xff0c;用于快速搭建kubernetes集群&#xff0c;目前应该是比较方便和推荐的&#xff0c;简单易用 kubea…

RDMA简介3之四种子协议对比

RDMA协议共有四种子协议&#xff0c;分别为InfiniBand、iWARP、RoCE v1和RoCE v2协议。这四种协议使用统一的RDMA API&#xff0c;但在具体的网络层级实现上有所不同&#xff0c;如图1所示&#xff0c;接下来将分别介绍这四种子协议。 图1 RDMA四种子协议网络层级关系图 Infin…

【最新版】西陆洗车系统源码全开源+uniapp前端+搭建教程

一.系统介绍 一款基于ThinkPHPUniapp开发的多门店洗车系统&#xff0c;包含用户端&#xff08;小程序&#xff09;、门店员工端&#xff08;小程序&#xff09;、门店端&#xff08;PC&#xff09;、平台管理端&#xff08;PC&#xff09;。 门店分连锁门店和独立门店&#xf…