开发者

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

目录
  • 前言
  • 一、Spring Boot 缓存核心机制概述
    • ✅ 缓存抽象原理
  • ️ 二、快速入门:开启缓存支持
    • Step 1:添加依赖(以 Redis 为例)
    • Step 2:启用缓存
  • 三、三大缓存注解详解与实战
    • ✅ 1. @Cacheable:查询缓存,避免重复计算
    • ✅ 2. @CachePut:更新缓存,保持一致性
    • ✅ 3. @CacheEvict:清除缓存,强制刷新
  • 四、进阶技巧与最佳实践
    • ✅ 1. 自定义缓存 Key 生成策略
    • ✅ 2. 设置缓存过期时间(以 Redis 为例)
    • ✅ 3. 多级缓存策略(Redis + Caffeine)
    • ✅ 4. 缓存穿透、击穿、雪崩解决方案
  • 五、综合案例:商品信息缓存实战
    • 六、常见问题与解决方案
      • 七、总结对比表
        • 八、结语

          前言

          在高并发、高性能的系统开发中,缓存是提升接口响应速度和降低数据库压力的重要手段。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 UsephprService {
              @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 总是会执行方法体,并将结果放入缓存;
          • 常用于新增、修改等写操作。
          www.devze.com

          ✅ 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 cacheManagerphp = 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;
              }www.devze.com
          }

          ✅ 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 Pr编程客栈oduct 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 或本地缓存进行实际应用;
          • 如何避免缓存穿透、雪崩、击穿等问题;
          • 如何提高系统的响应速度和稳定性。

          到此这篇关于Spring Boot 缓存注解详解:@Cacheable、@CachePut、@CacheEvict(超详细实战版)的文章就介绍到这了,更多相关Spring Boot @Cacheable、@CachePut、@CacheEvict内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

          0

          上一篇:

          下一篇:

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          最新开发

          开发排行榜