SpringBoot4与Redis8整合,利用@RedisListener注解简化消息监听,采用比较并交换机制实现乐观锁,使用DIGEST命令加速哈希计算,通过Duration定义过期时间,调用resetCaches安全清空缓存,并遵循分层架构与合理TTL设置,从而提升系统性能与数据一致性,有效应对高并发缓存竞争。
网上流传的整合教程大多还在讲解旧版本,这里将 Spring Boot 4 + Redis 8 最新版本的开发要点,连同新特性的用法,一起梳理清楚。
Spring Boot 4 正式发布(2025 年底),底层切换到 Spring Framework 7,Java 17 是起步要求,同时对 Jackson 3、Jakarta EE 11 做了统一升级。日常开发里,你能明显感受到两点变化:启动配置更模块化,以及 空安全注解(JSpecify) 让 IDE 提示更准。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
Redis 8 则在性能和新命令上持续发力。配合 Spring Data Redis 4.1,以前要自己拼 RedisMessageListenerContainer 的 Pub/Sub,现在可以像写 @KafkaListener 一样写 @RedisListener;Redis 8.4 新增的 Compare-and-Set、DIGEST 等命令,Spring 侧也已经跟上了。
一句话总结:新项目用 Spring Boot 4 + Redis 8,开发体验更顺,也不用背那么多样板代码。
一个典型的 Spring Boot 4 应用接入 Redis 8,可以分成三层来理解:

| 层级 | 职责 | 常用组件 |
|---|---|---|
| 应用层 | 处理业务请求、发消息、加缓存 | Controller、Service、@Cacheable、@RedisListener |
| 中间层 | 屏蔽 Redis 协议细节 | RedisTemplate、RedisCacheManager、Lettuce 连接池 |
| 存储层 | 真正存数据、跑命令 | Redis 8 的 String/Hash、Pub/Sub、Stream、JSON 模块 |
业务代码尽量只碰应用层和 Spring 提供的 API,别在 Service 里直接拼 Redis 命令字符串——后期换客户端、做单元测试都会轻松很多。
org.springframework.boot spring-boot-starter-parent 4.1.0 org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-cache org.apache.commons commons-pool2
spring:
data:
redis:
host: 127.0.0.1
port: 6379
password:
lettuce:
pool:
max-active: 16
max-idle: 8
min-idle: 2
max-wait: 3000ms
cache:
type: redis
redis:
time-to-live: 30m
cache-null-values: false
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJacksonJsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJacksonJsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
到这里,一个能跑起来的基础整合就完成了。下面进入本文重点——新特性怎么用。
以前的做法:手动创建 RedisMessageListenerContainer,注册 MessageListener,代码散在好几个类里。
现在的做法:加 @EnableRedisListeners,在方法上标 @RedisListener,跟 Kafka 那套几乎一样。

配置类:
@Configuration
@EnableRedisListeners
public class RedisListenerConfig {
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
消息发送方:
@Service
public class OrderNotifyService {
private final RedisMessageSendingTemplate sendingTemplate;
public OrderNotifyService(RedisMessageSendingTemplate sendingTemplate) {
this.sendingTemplate = sendingTemplate;
}
public void notifyOrderPaid(Long orderId) {
String payload = "{\"orderId\":" + orderId + ",\"status\":\"PAID\"}";
sendingTemplate.convertAndSend("order-notify", payload);
}
}
消息接收方:
@Component
public class OrderNotifyListener {
private static final Logger log = LoggerFactory.getLogger(OrderNotifyListener.class);
@RedisListener(topic = "order-notify")
public void onOrderPaid(String message) {
log.info("收到订单通知: {}", message);
// 这里写你的业务逻辑,比如更新本地状态、发站内信等
}
}
小提示:如果消息体是 JSON,可以在 @RedisListener(consumes = "application/json") 里声明类型,框架会自动选对应的转换器。
分布式场景里经常遇到这种需求:「只有当 key 当前值等于 A 时,才改成 B」。以前很多人写 Lua 脚本来保证原子性。
Redis 8.4 提供了 Compare-and-Set(CAS)和 Compare-and-Delete(CAD),Spring Data Redis 4.1 在 RedisTemplate 层已经封装好了。
场景示例:防止优惠券被重复领取
@Service
public class CouponService {
private final StringRedisTemplate stringRedisTemplate;
public CouponService(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 只有券状态为 AVAILABLE 时,才改成 CLAIMED
*/
public boolean claimCoupon(String couponId, String userId) {
String key = "coupon:" + couponId;
Boolean success = stringRedisTemplate.opsForValue()
.setIfPresent(key, "CLAIMED:" + userId,
Duration.ofHours(24));
return Boolean.TRUE.equals(success);
}
/**
* 只有值匹配时才删除(用于 token 失效场景)
*/
public boolean invalidateToken(String token) {
String key = "token:" + token;
return Boolean.TRUE.equals(
stringRedisTemplate.delete(key, token));
}
}
CAS 适合读多写少、冲突概率不高的场景。如果同一 key 并发极高,还是建议上 Redisson 分布式锁或数据库乐观锁字段。
Redis 8 新增的 DIGEST 命令,用 XXH3 算法算哈希,比自己在应用层算 MD5/SHA 再存进去更省事,也更适合做大文件或长字符串的指纹比对。
@Service
public class FileDigestService {
private final RedisTemplate redisTemplate;
public FileDigestService(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public String computeDigest(String content) {
// 调用 Redis 8 DIGEST 命令,服务端计算 XXH3 哈希
return redisTemplate.execute((RedisCallback) connection ->
connection.digest(content.getBytes(StandardCharsets.UTF_8)));
}
public boolean isDuplicate(String content) {
String digest = computeDigest(content);
String key = "file:digest:" + digest;
Boolean isNew = redisTemplate.opsForValue()
.setIfAbsent(key, "1", Duration.ofDays(7));
return !Boolean.TRUE.equals(isNew); // true 表示重复文件
}
}
典型用途:上传去重、缓存键生成、数据一致性抽检。
Spring Data Redis 4.1 开始,推荐用 Duration 和 Expiration 替代 TimeUnit 参数。语义更清晰,也不容易把「秒」和「毫秒」搞反。
// 旧写法(已标记过时)
// redisTemplate.expire("user:1001", 30, TimeUnit.MINUTES);
// 新写法(推荐)
redisTemplate.expire("user:1001", Duration.ofMinutes(30));
// 带过期条件的 SET
redisTemplate.opsForValue().set("session:abc", userInfo,
Expiration.from(Duration.ofHours(2)));
团队里如果有 Code Review 规范,可以直接约定:新代码一律用 Duration,老代码迁移时顺手改。
运维或测试环境经常需要「把所有缓存清掉」。以前要么 FLUSHDB(太狠),要么自己维护 key 列表。
Spring Data Redis 4.1 给 CacheManager 加了 resetCaches(),会按缓存区域批量清理,比全库 flush 安全。
@RestController
@RequestMapping("/admin/cache")
public class CacheAdminController {
private final CacheManager cacheManager;
public CacheAdminController(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@PostMapping("/reset")
public String resetAllCaches() {
if (cacheManager instanceof RedisCacheManager redisCacheManager) {
redisCacheManager.resetCaches();
return "缓存已重置";
}
return "当前 CacheManager 不支持 resetCaches";
}
}
注意:生产环境要给这个接口加权限控制,别裸奔在线上。

结合上图,几条实战经验:
1. 合理设置 TTL
缓存不是越久越好。热点数据 5~30 分钟,配置类数据 1~24 小时,按业务容忍的「脏读时间」来定。
2. 注意缓存穿透
查询不存在的数据时,别把 null 也缓存了(除非你能接受)。配置里 cache-null-values: false 是第一步,还可以对空结果缓存一个短 TTL 的占位值。
3. 更新策略选对
| 场景 | 推荐做法 |
|---|---|
| 读多写少 | @Cacheable + 过期自动失效 |
| 写后立即读 | @CachePut 或先更新 DB 再删缓存 |
| 数据一致性要求高 | 先更新 DB,再 @CacheEvict 删缓存 |
4. 示例:商品详情缓存
@Service
public class ProductService {
@Cacheable(value = "product", key = "#id")
public Product getById(Long id) {
return productMapper.selectById(id);
}
@CacheEvict(value = "product", key = "#product.id")
public void update(Product product) {
productMapper.updateById(product);
}
}
不需要一上来就搞集群,但下面几条建议尽早落地:
spring:
data:
redis:
timeout: 3s
connect-timeout: 3s
lettuce:
pool:
max-active: 32
max-idle: 16
min-idle: 4
shutdown-timeout: 200ms
连接池:Lettuce 默认共享连接,高并发场景务必开 commons-pool2,按 QPS 调整 max-active。
序列化:生产环境统一用 JSON 序列化(Jackson),方便排查问题时用 redis-cli 直接看内容。
监控:Spring Boot Actuator 配合 spring-boot-starter-data-redis 的健康检查,能及时发现 Redis 连不上的情况。
Key 规范:建议统一前缀,比如 app:module:biz:id,避免多服务共用一个 Redis 实例时互相踩 key。
Spring Boot 4 和 Redis 8 凑在一起,并不是「版本号 +1」那么简单。实际开发中值得优先用上的几点:
@RedisListener —— Pub/Sub 监听声明式写法,代码量明显减少FLUSHDB建议新项目直接以 Spring Boot 4.1 + Spring Data Redis 4.1 + Redis 8.4 作为基线。老项目如果还在 Boot 3.x,可以先把 Redis 客户端升上去,再规划 Boot 4 迁移——两条线并行,风险更小。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述