使用Spring Cache本地缓存示例代码

2025-08-19 10:50

本文主要是介绍使用Spring Cache本地缓存示例代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac...

一、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 China编程{

    /**
     * 创建并配置Caffeine缓存管理器
     *
     * @return CacheManager 实例,用于管理www.chinasem.cn应用中所有缓存
     *
     * 主要配置参数说明:
     * - 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: javascript条件表达式,当结果满足条件时不缓存
 */
@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(UChina编程ser user) {
    log.info("更新用户数据: {}", user.getId());
    return userRepository.save(user);
}

3.@CacheEvict - 删除缓存

/**
 * 删除用户(同时移除缓存)
 * @param id 用户ID
 * 
 * @CacheEvict 参数说明:
 * - beforeInvocation: 是否在方法执行前清除缓存(默认false)
 * - allEntries: 是否清空整个缓存区域(慎用)
 */
@Cwww.chinasem.cnacheEvict(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

总结 

到此这篇关于使用Spring Cache本地缓存的文章就介绍到这了,更多相关Spring Cache本地缓存内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于使用Spring Cache本地缓存示例代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155810

相关文章

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

Java实现复杂查询优化的7个技巧小结

《Java实现复杂查询优化的7个技巧小结》在Java项目中,复杂查询是开发者面临的“硬骨头”,本文将通过7个实战技巧,结合代码示例和性能对比,手把手教你如何让复杂查询变得优雅,大家可以根据需求进行选择... 目录一、复杂查询的痛点:为何你的代码“又臭又长”1.1冗余变量与中间状态1.2重复查询与性能陷阱1.

深度剖析SpringBoot日志性能提升的原因与解决

《深度剖析SpringBoot日志性能提升的原因与解决》日志记录本该是辅助工具,却为何成了性能瓶颈,SpringBoot如何用代码彻底破解日志导致的高延迟问题,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言第一章:日志性能陷阱的底层原理1.1 日志级别的“双刃剑”效应1.2 同步日志的“吞吐量杀手”

使用Python的requests库来发送HTTP请求的操作指南

《使用Python的requests库来发送HTTP请求的操作指南》使用Python的requests库发送HTTP请求是非常简单和直观的,requests库提供了丰富的API,可以发送各种类型的HT... 目录前言1. 安装 requests 库2. 发送 GET 请求3. 发送 POST 请求4. 发送

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

SpringBoot通过main方法启动web项目实践

《SpringBoot通过main方法启动web项目实践》SpringBoot通过SpringApplication.run()启动Web项目,自动推断应用类型,加载初始化器与监听器,配置Spring... 目录1. 启动入口:SpringApplication.run()2. SpringApplicat

Nginx中配置使用非默认80端口进行服务的完整指南

《Nginx中配置使用非默认80端口进行服务的完整指南》在实际生产环境中,我们经常需要将Nginx配置在其他端口上运行,本文将详细介绍如何在Nginx中配置使用非默认端口进行服务,希望对大家有所帮助... 目录一、为什么需要使用非默认端口二、配置Nginx使用非默认端口的基本方法2.1 修改listen指令

Python WebSockets 库从基础到实战使用举例

《PythonWebSockets库从基础到实战使用举例》WebSocket是一种全双工、持久化的网络通信协议,适用于需要低延迟的应用,如实时聊天、股票行情推送、在线协作、多人游戏等,本文给大家介... 目录1. 引言2. 为什么使用 WebSocket?3. 安装 WebSockets 库4. 使用 We

Java利用@SneakyThrows注解提升异常处理效率详解

《Java利用@SneakyThrows注解提升异常处理效率详解》这篇文章将深度剖析@SneakyThrows的原理,用法,适用场景以及隐藏的陷阱,看看它如何让Java异常处理效率飙升50%,感兴趣的... 目录前言一、检查型异常的“诅咒”:为什么Java开发者讨厌它1.1 检查型异常的痛点1.2 为什么说

python中的显式声明类型参数使用方式

《python中的显式声明类型参数使用方式》文章探讨了Python3.10+版本中类型注解的使用,指出FastAPI官方示例强调显式声明参数类型,通过|操作符替代Union/Optional,可提升代... 目录背景python函数显式声明的类型汇总基本类型集合类型Optional and Union(py