【SpringBoot实战指南】使用 Spring Cache

news2025/5/23 5:35:26

文章目录

  • 一、Spring Cache简介
    • 核心特点:
  • 二、基础配置
    • 1. 添加依赖
    • 2. 启用缓存
    • 3. 缓存配置方案
      • 方案1:通过 yml 配置文件
      • 方案2:自定义 Bean
  • 三、 缓存注解使用示例
    • 1.@Cacheable - 数据查询缓存
    • 2.@CachePut - 更新数据并缓存
    • 3.@CacheEvict - 删除缓存
    • 4. @Caching - 组合操作
    • 5. 条件缓存 (condition/unless)
    • 6. 异步缓存加载
  • 四、特殊场景处理
    • 1. 缓存空值防御
    • 2. 复合缓存键
  • 五、经验之谈

一、Spring Cache简介

Spring Cache 是 Spring 框架提供的一套声明式缓存抽象层,通过注解方式简化缓存操作,它通过在方法上添加注解(如 @Cacheable、@CacheEvict)来地管理缓存操作,无需手动编写缓存逻辑。

它支持多种缓存实现(如 Caffeine、Redis、EhCache),并统一了缓存访问的 API。

这里需要注意两点:

  1. Spring Cache 只是一个声明式的抽象缓存层,意思是它只提供了接口,不提供实现
  2. 具体的实现可以有很多,比如 Caffeine,Redis,EhCache 这些,只要实现了这些接口,就可以被 Spring Cache 使用。

核心特点:

  • 基于注解的声明式缓存
  • 支持 SpEL 表达式
  • 自动与 Spring 生态集成
  • 支持条件缓存

二、基础配置

1. 添加依赖

<!-- Spring Boot Cache Starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!-- 如果使用Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 如果使用caffeine -->
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

2. 启用缓存

在启动类添加@EnableCaching注解:

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

3. 缓存配置方案

方案1:通过 yml 配置文件

spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=500,expireAfterWrite=60s
    # 或者分开配置
    cache-names: users,products
    caffeine.spec: # 全局默认配置
      maximumSize=1000,
      expireAfterAccess=30m

方案2:自定义 Bean

@Configuration  // 标记这是一个Spring配置类
public class CacheConfig {

    /**
     * 创建并配置Caffeine缓存管理器
     *
     * @return CacheManager 实例,用于管理应用中所有缓存
     *
     * 主要配置参数说明:
     * - initialCapacity: 初始缓存空间大小(提升初始性能)
     * - maximumSize: 缓存最大容量(基于条目数)
     * - expireAfterWrite: 写入后过期时间(数据一致性优先场景)
     * - recordStats: 开启统计功能(用于监控和调优)
     */
    @Bean
    public CacheManager cacheManager() {
        // 创建Caffeine缓存管理器实例
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();

        // 配置Caffeine缓存参数
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(100)      // 初始容量100个条目
                .maximumSize(1000)          // 最大缓存1000个条目,超过后按LRU淘汰
                .expireAfterWrite(10, TimeUnit.MINUTES)  // 写入10分钟后过期
                .recordStats());           // 启用缓存统计(命中率等)

        return cacheManager;
    }

    /**
     * 创建短期缓存实例(独立于主缓存管理器)
     *
     * @return Cache 实例,适用于高频访问的临时数据
     *
     * 典型使用场景:
     * - 高频访问的临时数据
     * - 需要快速失效的验证码等
     * - 与其他缓存不同生命周期的数据
     */
    @Bean(name = "shortTermCache")  // 指定Bean名称便于按名称注入
    public Cache shortTermCache() {
        return Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.MINUTES)  // 1分钟过期(短期存储)
                .maximumSize(100)                       // 最大100个条目
                .build();                               // 构建Cache实例
    }
}

三、 缓存注解使用示例

  • @Cacheable:用于标记方法,表示该方法将结果缓存起来,下次调用时直接从缓存中获取结果,而不需要重新执行方法。
  • @CacheEvict:用于标记方法,表示该方法将清除缓存,通常用于删除缓存。
  • @CachePut:用于标记方法,表示该方法将更新缓存,通常用于更新缓存。
  • @Caching:用于组合多个缓存注解,可以同时使用多个缓存注解。
  • @CacheConfig:用于标记类,表示该类中的所有方法将使用指定的缓存配置。

1.@Cacheable - 数据查询缓存

/**
 * 根据ID获取用户信息(带缓存)
 * @param id 用户ID
 * @return 用户对象,如果不存在返回null
 * 
 * @Cacheable 参数说明:
 * - value/cacheNames: 指定缓存名称(对应Caffeine配置)
 * - key: 缓存键,使用SpEL表达式(#参数名引用方法参数)
 * - unless: 条件表达式,当结果满足条件时不缓存
 */
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
    log.info("执行数据库查询,用户ID: {}", id);
    return userRepository.findById(id).orElse(null);
}

2.@CachePut - 更新数据并缓存

/**
 * 更新用户信息(同时更新缓存)
 * @param user 用户对象
 * @return 更新后的用户对象
 * 
 * @CachePut 特点:
 * - 总是执行方法体
 * - 用返回值更新缓存
 * - 适用于"先写后读"场景
 */
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
    log.info("更新用户数据: {}", user.getId());
    return userRepository.save(user);
}

3.@CacheEvict - 删除缓存

/**
 * 删除用户(同时移除缓存)
 * @param id 用户ID
 * 
 * @CacheEvict 参数说明:
 * - beforeInvocation: 是否在方法执行前清除缓存(默认false)
 * - allEntries: 是否清空整个缓存区域(慎用)
 */
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
    log.info("删除用户: {}", id);
    userRepository.deleteById(id);
}

4. @Caching - 组合操作

组合操作允许在单个方法上同时使用多个缓存注解,以实现更复杂的缓存策略。

/**
* 更新用户状态(复杂缓存操作)
* @param userId 用户ID
* @param status 新状态
*
* 典型场景:
* - 更新用户缓存
* - 同时失效用户列表缓存
    */
    @Caching(
    put = @CachePut(value = "users", key = "#userId"),
    evict = @CacheEvict(value = "userList", allEntries = true)
    )
    public void updateUserStatus(Long userId, UserStatus status) {
    log.info("更新用户{}状态为{}", userId, status);
    userRepository.updateStatus(userId, status);
    }

5. 条件缓存 (condition/unless)

条件缓存允许在缓存注解中添加 SpEL 条件表达式,以控制缓存的触发时机。

/**
 * 获取用户详情(带条件缓存)
 * @param id 用户ID
 * 
 * 缓存条件说明:
 * - condition: 只有id>1000时才走缓存
 * - unless: 结果中status=DELETED时不缓存
 */
@Cacheable(value = "users", 
           key = "#id",
           condition = "#id > 1000",
           unless = "#result != null && #result.status == T(com.example.UserStatus).DELETED")
public User getUserDetail(Long id) {
    log.info("查询用户详情: {}", id);
    return userRepository.findDetailById(id);
}

6. 异步缓存加载

/**
 * 获取用户订单列表(异步缓存)
 * @param userId 用户ID
 * 
 * @sync = true 表示:
 * - 多线程并发时,只有一个线程会执行加载
 * - 其他线程等待结果
 */
@Cacheable(value = "orders", key = "#userId", sync = true)
public List<Order> getUserOrders(Long userId) {
    log.info("加载用户{}订单数据...", userId);
    return orderService.getOrdersByUser(userId);
}

四、特殊场景处理

1. 缓存空值防御

/**
 * 查询用户(防穿透处理)
 * @param name 用户名
 * 
 * 特殊处理:
 * - 对null结果也进行缓存(特殊标记对象)
 * - 设置较短过期时间(配置文件中定义)
 */
@Cacheable(value = "usersByName", 
           key = "#name",
           unless = "#result == null || #result == T(com.example.CacheConstants).NULL_OBJECT")
public User getUserByName(String name) {
    User user = userRepository.findByName(name);
    return user != null ? user : CacheConstants.NULL_OBJECT;
}

2. 复合缓存键

/**
 * 获取用户在某系统的权限列表
 * @param userId 用户ID 
 * @param systemCode 系统编码(如:"OA", "CRM"等)
 * @return 权限字符串集合
 * 
 * 缓存Key设计说明:
 * 1. 使用复合Key结构:`用户ID_系统编码`(如:123_OA)
 * 2. 优点:
 *    - 避免不同系统权限缓存冲突
 *    - 支持按用户+系统维度独立管理缓存
 * 3. 缓存条件:仅当结果非空时缓存
 */
@Cacheable(value = "userPermissions", 
           key = "#userId + '_' + #systemCode",
           unless = "#result == null || #result.isEmpty()")
public Set<String> getUserSystemPermissions(Long userId, String systemCode) {
    log.debug("查询用户[{}]在系统[{}]的权限", userId, systemCode);
    return permissionService.findPermissions(userId, systemCode);
}

/**
 * 获取用户角色列表(带枚举参数的Key示例)
 * @param userId 用户ID
 * @param roleType 角色类型枚举
 * 
 * 枚举类型处理技巧:
 * 1. 调用枚举的name()方法转换为字符串
 * 2. 最终Key格式:`userId:roleType`(如:123:ADMIN)
 */
@Cacheable(value = "userRoles", 
           key = "#userId + ':' + #roleType.name()")
public List<Role> getUserRoles(Long userId, RoleType roleType) {
    return roleService.findByUserAndType(userId, roleType);
}

五、经验之谈

  • 推荐为每个 @Cacheable 方法添加 unless 条件防御 null
  • 业务更新方法建议同时使用 @CachePut@CacheEvict
  • 高频访问数据考虑设置 sync=true

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

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

相关文章

centos8 配置网桥,并禁止kvm默认网桥

环境背景&#xff1a; 我使用vmware部署了一台kvm服务器&#xff0c;网络模式是nat。我想要kvm创建的虚拟机可以访问公网&#xff1b;所以kvm默认的地址不行&#xff0c;我必须使用nat地址才可以&#xff1b; 实现方式&#xff1a; 创建一个网桥&#xff0c;将本地的网络接口…

【Node.js】全栈开发实践

个人主页&#xff1a;Guiat 归属专栏&#xff1a;node.js 文章目录 1. Node.js 全栈开发概述1.1 全栈开发的优势1.2 Node.js 全栈开发技术栈 2. 开发环境搭建2.1 Node.js 和 npm 安装2.2 开发工具安装2.3 版本控制设置2.4 项目初始化流程 3. 后端开发 (Node.js)3.1 Express 框架…

ubuntu sh安装包的安装方式

ubuntu sh安装包的安装方式以Miniconda2为例 https://repo.anaconda.com/miniconda/ 如果需要python2.7版本可下载以下版本 Miniconda2-latest-Linux-x86_64.sh 打开终端输入安装命令 sudo sh Miniconda2-latest-Linux-x86_64.sh 然后按提示安装&#xff0c;注意安装位置 …

OpenAI宣布:核心API支持MCP,助力智能体开发

今天凌晨&#xff0c;OpenAI全资收购io的消息成为头条。同时&#xff0c;OpenAI还宣布其核心API——Responses API支持MCP服务。过去&#xff0c;开发智能体需通过函数调用与外部服务交互&#xff0c;过程复杂且延迟高。而今&#xff0c;Responses API支持MCP后&#xff0c;开发…

微服务中的 AKF 拆分原则:构建可扩展系统的核心方法论

在数字化浪潮的推动下&#xff0c;互联网应用规模呈指数级增长&#xff0c;传统单体架构逐渐暴露出难以扩展、维护成本高等问题&#xff0c;微服务架构应运而生并成为企业应对复杂业务场景的主流选择。然而&#xff0c;随着业务的不断扩张和用户量的持续增加&#xff0c;如何确…

vue element-plus 集成多语言

main.js中 // 引入i18n import i18n from /i18n/index 使用i18 app.use(i18n) 在App.vue中 <template><el-config-provider :locale"locale" namespace"el" size"small"><router-view /></el-config-provider> </tem…

如何测试JWT的安全性:全面防御JSON Web Token的安全漏洞

在当今的Web应用安全领域&#xff0c;JSON Web Token(JWT)已成为身份认证的主流方案&#xff0c;但OWASP统计显示&#xff0c;错误配置的JWT导致的安全事件占比高达42%。本文将系统性地介绍JWT安全测试的方法论&#xff0c;通过真实案例剖析典型漏洞&#xff0c;帮助我们构建全…

车载网关策略 --- 车载网关重置前的请求转发机制

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

EtpBot:安卓自动化脚本开发神器

EtpBot 是什么&#xff1f; EtpBot是一款专为安卓设备设计的自动化脚本开发工具&#xff0c;支持用户通过编写脚本实现自动化操作。该模块提供了丰富的API接口&#xff0c;涵盖点击、滑动、输入、截图等常见操作&#xff0c;帮助开发者快速构建自动化任务。ETPBot支持多设备并行…

连锁企业管理系统对门店运营的促进作用

连锁企业管理系统通过整合数字化工具与流程优化&#xff0c;能从多维度提升门店运营效率与竞争力&#xff0c;以下是其对门店运营的具体促进作用&#xff1a; 一、数据化管理&#xff1a;精准决策与运营监控 实时数据同步与分析 系统可整合各门店销售数据、库存信息、客流统计…

现代生活健康养生新策略

在充满挑战的现代生活中&#xff0c;各种健康问题悄然来袭&#xff0c;亚健康状态困扰着不少人。摒弃中医概念&#xff0c;运用现代科学理念&#xff0c;也能找到行之有效的养生之道。​ 饮食上&#xff0c;遵循 “彩虹饮食法” 能让营养摄入更全面。不同颜色的蔬果富含不同的…

车载以太网网络测试-27【SOME/IP-SD简述】

文章目录 1 摘要2 SOME/IP-SD协议介绍2.1 定义与作用2.2 SOMEIP/SD协议通俗易懂的理解2.2.1 SOMEIP/SD协议是什么&#xff1f;2.2.2 通信流程&#xff08;简化&#xff09;2.2.3 车载功能示例2.2.4 类比理解 2.3 SOME/IP-SD报文结构2.3.1 Flags2.3.1.1 REBOOT (Bit 7)2.3.1.2 U…

【Redis8】最新安装版与手动运行版

1. 下载 Redis 百度网盘 2. 解压后直接运行 redis-server.exe 3. 使用安装版 双击 install_redis_service.bat 输入安装路径&#xff08;请提前创建好安装路径&#xff09;后直接回车下一步直接回车即可&#xff0c;因为是使用配置模板文件为默认解压出来的&#xff0c;然后…

Spring Boot 集成 Elasticsearch【实战】

前言&#xff1a; 上一篇我们简单分享了 Elasticsearch 的一些概念性的知识&#xff0c;本篇我们来分享 Elasticsearch 的实际运用&#xff0c;也就是在 Spring Booot 项目中使用 Elasticsearch。 Elasticsearch 系列文章传送门 Elasticsearch 基础篇【ES】 Elasticsearch …

06算法学习_58. 区间和

58. 区间和 06算法学习_58. 区间和题目描述&#xff1a;个人代码&#xff1a;学习思路&#xff1a;第一种写法&#xff1a;题解关键点&#xff1a; 个人学习时疑惑点解答&#xff1a; 06算法学习_58. 区间和 卡码网题目链接: 59. 螺旋矩阵 II 题目描述&#xff1a; 58. 区间…

Python爬虫之路(14)--playwright浏览器自动化

playwright 前言 ​ 你有没有在用 Selenium 抓网页的时候&#xff0c;体验过那种「明明点了按钮&#xff0c;它却装死不动」的痛苦&#xff1f;或者那种「刚加载完页面&#xff0c;它又刷新了」的抓狂&#xff1f;别担心&#xff0c;你不是一个人——那是 Selenium 在和现代前…

Python开启智能之眼:OpenCV+深度学习实战

开篇导言 场景痛点 "某汽车零部件厂每月因人工质检遗漏损失300万,直到部署了基于Python的视觉检测系统..." 传统质检效率低下、成本高昂 深度学习技术带来的产业变革 Python在视觉识别领域的独特优势 一、技术架构解析 1.1 系统组成模块 图表 代码 下载 检测结…

华为模拟器练习简单的拓扑图(3台路由器和2台pc)

1、题目要求 根据下图&#xff0c;pc1连通pc2&#xff0c;实现不同网段直接的互通 2、思路整理 2.1 根据图上的要求&#xff0c;为主机和路由器相连接的端口设置对应IP地址&#xff08;子网掩码都是24位&#xff09;,路由器连接pc的那个端口&#xff0c;是主机pc的网关 2.2 …

uniapp生成的app,关于跟其他设备通信的支持和限制

以下内容通过AI生成&#xff0c;这里做一下记录。 蓝牙 移动应用&#xff08;App&#xff09;通过蓝牙与其他设备通信&#xff0c;是通过分层协作实现的。 一、通信架构分层 应用层&#xff08;App&#xff09; 调用操作系统提供的蓝牙API&#xff08;如Android的BluetoothA…

Proxmox 主机与虚拟机全部断网问题排查与解决记录

Proxmox 主机与虚拟机全部断网问题排查与解决记录 关键词&#xff1a;Proxmox、e1000e、板载网卡、断网、网络桥接、Hardware Unit Hang、网卡挂死 背景 近期在使用 Proxmox VE 管理服务器时&#xff0c;遇到一个奇怪的问题&#xff1a;每当在某个虚拟机中执行某些操作&#x…