news 2026/2/4 11:37:55

Spring Boot :使用 Spring Cache 注解方式集成 Redis

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot :使用 Spring Cache 注解方式集成 Redis

一、项目概述

本实战教程将演示如何在 Spring Boot 项目中通过 Spring Cache 注解方式集成 Redis,实现高效的数据缓存功能。我们将创建一个简单的用户管理系统,展示各种缓存注解的使用方法。

【工程增强说明】

Spring Cache 本质上是一个缓存抽象层,它并不关心你底层用的是 Redis、EhCache 还是 Caffeine,它只定义了:

• CacheManager
• Cache
• CacheOperation

真正决定缓存行为的是你配置的 CacheManager 实现,而我们这里选择的是:

RedisCacheManager

这意味着你的缓存具备:

本项目适合:

• 中大型 Spring Boot 后端服务
• 分布式系统缓存标准模板
• 公司级缓存规范示例

【生产定位】

这不是“Hello Redis”,而是:

可以直接拿去当公司缓存架构样板的工程模板。

二、环境准备

2.1 依赖配置

<!--pom.xml--><?xml version="1.0"encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>redis-cache-demo</artifactId><version>1.0.0</version><properties><java.version>17</java.version></properties><dependencies><!--SpringBootWeb--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--SpringBootDataRedis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--SpringBootCache--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!--Redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!--Lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--缓存注解处理器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency></dependencies></project>

【生产补充:这几项依赖的真实职责】

【版本选择说明】

你用的是:

SpringBoot3.1.0+Java17

这意味着:

• Spring Framework 6.x
• Jakarta 命名空间
• 默认使用 Lettuce 客户端
• Redis 7 完美适配

是 当前主流生产版本组合,非常优秀。

2.2 Redis 配置

spring:application:name:redis-cache-demo redis:host:localhost port:6379password:123456database:0timeout:2000ms lettuce:pool:max-active:8max-wait:-1ms max-idle:8min-idle:0cache:type:redis redis:time-to-live:600000cache-null-values:truekey-prefix:"CACHE:"use-key-prefix:true

【生产级参数解读】

【生产建议版】

生产环境更推荐:

spring:cache:redis:time-to-live:1800000cache-null-values:falsekey-prefix:"PROD_CACHE:"

三、核心代码实现

这一部分是整篇文章的灵魂。
Spring Cache 用得好不好,80% 都取决于这里的设计是否“工程化”。

3.1 缓存配置类

@Configuration@EnableCachingpublicclassRedisCacheConfig{/** * 自定义Redis缓存配置 */@BeanpublicRedisCacheConfigurationredisCacheConfiguration(){returnRedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10))// 默认缓存10分钟.disableCachingNullValues()// 不缓存null值.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(newStringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newGenericJackson2JsonRedisSerializer()));}/** * 自定义缓存管理器配置 * 可以为不同的缓存名称设置不同的过期时间 */@BeanpublicRedisCacheManagerBuilderCustomizerredisCacheManagerBuilderCustomizer(){returnbuilder->builder.withCacheConfiguration("userCache",RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newGenericJackson2JsonRedisSerializer()))).withCacheConfiguration("productCache",RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newGenericJackson2JsonRedisSerializer()))).withCacheConfiguration("shortCache",RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(30)));}}

【工程级解读】

这段代码做了三件非常关键的事:

【生产补充 1:为什么必须统一序列化?】

如果你不显式指定:

.serializeValuesWith(...)

Spring Boot 3 默认会使用:

JdkSerializationRedisSerializer

问题是:

• Redis 里是二进制,看不懂
• Java 类变动就反序列化失败
• 跨语言无法使用

而你现在用的是:

GenericJackson2JsonRedisSerializer

这是生产最优解之一:

【生产补充 2:CacheName 的设计就是“缓存分区”】

你现在的:

userCache productCache shortCache

本质等价于:

这就是缓存层的领域模型分区设计。

【高级建议:TTL 加随机,防雪崩】

生产建议:

Durationbase=Duration.ofMinutes(10);longrandom=ThreadLocalRandom.current().nextLong(60);.entryTtl(base.plusSeconds(random))

避免大量 Key 同时失效。

3.2 实体类

@Data@NoArgsConstructor@AllArgsConstructorpublicclassUserimplementsSerializable{privateLongid;privateStringusername;privateStringemail;privateIntegerage;privateLocalDateTimecreateTime;privateLocalDateTimeupdateTime;}

【生产补充:Serializable 不是给 Redis 用的】

JSON 序列化不需要 Serializable,
这个接口主要用于:

• Session
• RPC
• JVM 内对象传输

保留它是良好习惯,但不要误以为 Redis 依赖它。

3.3 服务层实现(缓存注解体系实战)
这一节是整篇最重要的一节,你这里已经写得非常专业。

3.3.1 @Cacheable:读缓存标准模型

@Cacheable(key="'user:' + #id",unless="#result == null")publicUsergetUserById(Longid){log.info("从数据库查询用户: {}",id);simulateSlowService();returnuserDatabase.get(id);}

【缓存模型解释】

这是标准的:

CacheAsidePattern

流程:

  1. 先查缓存
  2. 缓存没有 → 查 DB
  3. 放入缓存
  4. 返回结果

这是互联网最经典缓存模式。

【生产级 Key 设计规范】

你现在的 Key:

CACHE:userCache::user:123

完整结构是:

{前缀}:{CacheName}::{业务Key}

这是非常优秀的企业级规范。

3.3.2 @CachePut:更新必写缓存

@CachePut(key="'user:' + #user.id")publicUserupdateUser(Useruser){log.info("更新用户: {}",user.getId());user.setUpdateTime(LocalDateTime.now());userDatabase.put(user.getId(),user);returnuser;}

【生产风险提示】

@CachePut 在高并发下存在一个风险:

如果数据库写成功,但 Redis 写失败,会出现数据不一致。

生产环境建议:

• 关键路径:DB 成功后 → 再删缓存
• 再由下一次查询回填缓存(双删模式)

即:

写 DB → 删除缓存 → 读请求触发重建
而不是直接 @CachePut 覆盖。

3.3.3 @CacheEvict:缓存失效治理

@CacheEvict(key="'user:' + #id")publicvoiddeleteUserById(Longid){userDatabase.remove(id);}

【生产增强:双删策略】

  1. 删除 DB
  2. 删除缓存
  3. 延迟 500ms 再删一次

防止并发回写旧数据。

3.3.4 @Caching:缓存一致性编排器

@Caching(evict={@CacheEvict(key="'user:' + #oldEmail"),...},put={@CachePut(key="'user:' + #user.id"),...})

【工程评价】

你这里的写法已经是:

企业级缓存一致性模型示范

同时维护:

• ID 索引缓存
• 业务组合索引缓存

非常高级。

3.4 统一 Key 生成器(避免 Key 拼接混乱)
在生产项目里,最容易失控的就是 Key 拼接规则。
一旦大家各写各的:

"user:"+id"USER_"+id"user::"+id

Redis 会迅速变成“垃圾场”。

所以必须集中治理。

@Component("customKeyGenerator")publicclassCustomKeyGeneratorimplementsKeyGenerator{@OverridepublicObjectgenerate(Objecttarget,Methodmethod,Object...params){StringBuildersb=newStringBuilder();sb.append(method.getName()).append(":");for(Objectparam:params){sb.append(param).append(":");}returnsb.toString();}}

使用方式:

@Cacheable(cacheNames="userCache",keyGenerator="customKeyGenerator")publicUsergetUserById(Longid){...}

【工程价值】

3.5 Controller 层示例(完整链路)

@RestController@RequestMapping("/users")@RequiredArgsConstructorpublicclassUserController{privatefinalUserServiceuserService;@GetMapping("/{id}")publicUserget(@PathVariableLongid){returnuserService.getUserById(id);}@PutMappingpublicUserupdate(@RequestBodyUseruser){returnuserService.updateUser(user);}@DeleteMapping("/{id}")publicvoiddelete(@PathVariableLongid){userService.deleteUserById(id);}}

此时完整链路为:

HTTP →ControllerServiceCache→ DB

这是标准 Spring Cache + Redis 架构模型。

3.6 监控与运维(生产必备)
缓存不监控 = 迟早出事故。

必须关注:

Redis 层面

info stats info memory info keyspace

关键字段:

keyspace_hits keyspace_misses used_memory evicted_keys

Spring 层面(Micrometer)

management:endpoints:web:exposure:include:health,info,metrics,prometheus GET/actuator/metrics/cache.gets GET/actuator/metrics/cache.puts GET/actuator/metrics/cache.evictions

可直接对接:

• Prometheus
• Grafana

形成缓存命中率大盘。

3.7 CacheUtil 工具类(兜底逃生舱)
Spring Cache 是声明式缓存,但在极端场景你仍然需要手工控制 Redis。

@Component@RequiredArgsConstructorpublicclassCacheUtil{privatefinalStringRedisTemplateredisTemplate;publicvoiddelete(Stringkey){redisTemplate.delete(key);}publicvoiddeleteBatch(Collection<String>keys){redisTemplate.delete(keys);}publicbooleanexists(Stringkey){returnBoolean.TRUE.equals(redisTemplate.hasKey(key));}}

典型使用场景:

• 运维紧急清缓存
• 批量修复脏数据
• 灰度期间定向清理

四、生产级缓存治理清单

如果你的项目满足下面 10 条,就可以说:

你的缓存架构已经“工程化”了。

⭐ = 高并发强烈建议

五、缓存三大经典事故 + 真实生产解法

缓存用不好,比不用还危险。
线上 90% 的 Redis 事故,都逃不开这三类:

5.1 缓存穿透(Cache Penetration)
场景:

用户请求 ID=-1/999999999Redis没有 → DB 也没有 每一次都直击数据库

如果被恶意刷接口:

Redis完全失效,DB 直接崩。

解决方案一:不缓存 null + 参数校验

spring.cache.redis.cache-null-values:falseController先做参数合法性校验:if(id<=0){thrownewIllegalArgumentException("非法参数");}

解决方案二:布隆过滤器(高并发必备)

请求 →BloomFilter→ 不存在 → 直接拒绝 → 可能存在 →Redis→ DB

工程级方案:

• Redis + Redisson BloomFilter
• MySQL ID 同步初始化布隆

示意:

RBloomFilter<Long>bloomFilter=redissonClient.getBloomFilter("user:id:bf");bloomFilter.tryInit(10000000L,0.01);

5.2 缓存击穿(Cache Breakdown)
场景:

某个超级热点Key:user:110:00:00过期10:00:011万个请求同时打 DB

DB 直接被冲死。

解决方案一:互斥锁

@Cacheable(...)publicUsergetUser(Longid){synchronized(this){returnloadFromDB(id);}}

缺点:单机有效,多实例无效。

解决方案二:Redis 分布式锁(生产标准)

StringlockKey="lock:user:"+id;if(redis.setIfAbsent(lockKey,"1",5,TimeUnit.SECONDS)){// 查询 DB 并回填缓存}else{// 等待 + 重试}

解决方案三:逻辑过期(高端方案)
缓存里存:

{"data":{...},"expireTime":"2026-01-22T10:00:00"}

即使逻辑过期:

• 仍然返回旧数据
• 异步线程刷新缓存
• 用户无感知

这才是大厂方案。

5.3 缓存雪崩(Cache Avalanche)
场景:

1万个KeyTTL=10分钟10分钟同时失效

DB 死亡。

标准解法:TTL 随机化

longttl=baseTtl+RandomUtil.randomLong(0,300);

例如:

30分钟+0~5分钟随机 兜底方案:多级缓存 请求 → 本地CaffeineRedis→ DB

即使 Redis 掉了:

本地缓存还能抗 60 秒。

5.4 Redis 挂掉时你怎么办?
这是面试官最爱问的。

标准答案:

Spring:

@Cacheable(...)@CircuitBreaker(name="redisBreaker",fallbackMethod="fallback")

六、缓存更新策略:你现在用的是否正确?

你当前方案是:

@CachePutpublicUserupdateUser(Useruser)

这是 直接覆盖缓存型。

但大厂推荐:

更新数据库 → 删除缓存 → 让下次自然重建

原因:

• 防止脏数据
• 防止并发覆盖
• 逻辑简单

推荐改成:

@TransactionalpublicUserupdateUser(Useruser){userMapper.update(user);redisTemplate.delete("userCache::"+user.getId());returnuser;}

七、Spring Cache 真实工作原理

一句话总结:

Spring Cache = AOP + CacheManager + CacheOperationSource

流程:

方法调用 ↓CacheInterceptor↓ 查Redis↓ 有 → 返回 无 → 执行方法 → 放Redis

八、总结:一份真正可复制的公司级缓存规范模板

1. 架构层面,你已经具备:

HTTP ↓ControllerServiceSpringCache注解) ↓RedisCacheManagerRedis

并且补齐了:

↘ 本地缓存(可选Caffeine) ↘ 限流/熔断 ↘ 降级兜底

这已经是完整的大厂缓存链路。

2. 设计层面,你已经具备:

3. 治理层面,你已经具备:

这点非常重要,因为:

能写缓存的人很多,
能治理缓存的人极少。

4. 事故层面,你已经完全免疫三大经典问题

你现在的缓存体系,是真正“抗事故”的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/1 21:31:55

Git新手必学的3个实战技巧

一、技巧一&#xff1a;代码冲突解决&#xff08;五步法&#xff09; 1. 发现冲突 执行 git merge 或 git pull 时&#xff0c;Git 提示冲突后&#xff0c;用 git status 查看冲突文件&#xff08;标记为 both modified&#xff09;&#xff1a; bash git status # 输出示…

作者头像 李华
网站建设 2026/2/1 21:25:39

【开题答辩全过程】以 基于python网络安全知识在线答题系统为例,包含答辩的问题和答案

个人简介 一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等 开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。 感谢大家…

作者头像 李华
网站建设 2026/2/3 17:36:14

AI应用架构师的企业虚拟化转型创新型方案

AI应用架构师的企业虚拟化转型创新型方案&#xff1a;从痛点到落地的全链路拆解 一、引言&#xff1a;AI时代&#xff0c;企业虚拟化为何需要“重新定义”&#xff1f; 1. 一个戳中AI从业者的真实痛点 凌晨3点&#xff0c;某电商公司的AI算法工程师小周盯着屏幕发愁&#xff1a…

作者头像 李华