Spring Boot 3 默认不自动配置RedisMessageListenerContainer,必须手动声明Bean并显式设置ConnectionFactory、TaskExecutor、addMessageListener及setAutoStartup(true),否则监听器不启动且无日志。

Spring Boot 3 默认使用 Lettuce 作为 Redis 客户端,发布订阅配置必须显式启用监听容器,且不能依赖自动装配的 RedisMessageListenerContainer —— 它不会自动创建,必须手动声明 Bean。
为什么 RedisMessageListenerContainer 不自动生效
Spring Boot 3 移除了对 RedisMessageListenerContainer 的自动配置支持(spring-boot-starter-data-redis 不再触发 @EnableRedisHttpSession 或监听器容器初始化)。即使你写了 @Configuration 类,若没显式注册 RedisMessageListenerContainer Bean,监听器永远不会启动。
- 现象:
onMessage方法从不被调用,日志无任何订阅记录,redis-cli SUBSCRIBE xxx却能收到消息 → 说明 Redis 服务正常,但 Spring 侧未建立监听连接 - 根本原因:Lettuce 的连接是基于事件循环(EventLoop)的,
RedisMessageListenerContainer需要绑定到一个活跃的RedisConnectionFactory并主动调用start();Spring Boot 3 不再代劳 - 关键点:必须确保
RedisConnectionFactory是 Lettuce 类型(非 Jedis),且已正确配置连接池与线程模型
如何正确声明 RedisMessageListenerContainer Bean
手动构建容器时,需显式设置连接工厂、线程执行器(否则默认用同步单线程,会阻塞)、并注册监听器适配器与 Topic。不要复用 StringRedisTemplate 的序列化器,发布订阅默认走字节流,推荐统一用 StringRedisTemplate 的 connectionFactory 和 valueSerializer。
- 必须调用
container.setTaskExecutor(...),否则消息回调会在主线程(如 Web 请求线程)中执行,极易引发超时或阻塞 - Topic 类型优先选
PatternTopic(支持通配符匹配,如"user:*),而非ChannelTopic(仅精确匹配);两者不可混用在同一个容器中 -
MessageListenerAdapter构造时,第二个参数必须是接收方法名(字符串),该方法签名必须为void method(Message, byte[])或void method(String)(后者需开启serializer自动转换) - 示例片段:
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
MessageListenerAdapter messageListenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setTaskExecutor(new ThreadPoolTaskExecutor() {{
setCorePoolSize(4);
setMaxPoolSize(8);
setQueueCapacity(100);
setThreadNamePrefix("redis-listener-");
}});
container.addMessageListener(messageListenerAdapter, new PatternTopic("event:*"));
return container;
}
Lettuce 连接工厂的配置陷阱
Spring Boot 3 中 RedisConnectionFactory 默认由 LettuceClientConfigurationBuilderCustomizer 管理,但监听场景下容易忽略两个关键定制点:
-
setShareNativeConnection(false):必须设为false,否则 Lettuce 复用连接会导致订阅通道被意外关闭或消息丢失 -
ClientOptions.builder().publishOnScheduler(true):若监听高吞吐消息,建议启用调度器,避免 Netty EventLoop 被阻塞 - 连接池配置中,
max-active建议 ≥ 监听器数量 + 1(至少留一个连接给发布者),否则可能因连接争抢导致订阅延迟 - YAML 示例(注意路径是
spring.data.redis.lettuce,不是旧版spring.redis.lettuce):
spring:
data:
redis:
host: localhost
port: 6379
lettuce:
pool:
max-active: 12
max-idle: 8
client-options:
publish-on-scheduler: true
监听器方法反序列化失败怎么办
默认 Message.getBody() 是原始字节数组,若你用 StringRedisTemplate.convertAndSend() 发布 JSON 字符串,监听器里直接 new String(msg.getBody()) 可能乱码或失败 —— 因为 Lettuce 默认未指定字符集,且未处理空值/二进制边界。
- 安全做法:在
MessageListenerAdapter初始化时传入StringRedisTemplate.getValueSerializer(),让框架自动转成String - 更健壮方式:监听器方法签名为
void onEvent(String message),并在@Bean MessageListenerAdapter中指定setSerializer(stringRedisTemplate.getValueSerializer()) - 若发布的是自定义对象(如
LoginSuccessMessage),必须确保监听器和发布者使用完全相同的RedisTemplate<String, T>实例,且序列化器一致(如Jackson2JsonRedisSerializer) - 错误典型:
Cannot construct instance of `xxx` (no Creators, like default constructor, exist)→ 检查 DTO 是否有无参构造、字段是否 public 或有 getter/setter
真正容易被忽略的是:Lettuce 的订阅连接是长生命周期的,一旦容器启动后,它会持续保活;但若 Redis 服务重启或网络闪断,RedisMessageListenerContainer 默认不会自动重连订阅 —— 你需要自己监听 ConnectionFailureEvent 或引入重试逻辑。这不是配置问题,而是生产环境必须面对的可靠性缺口。


















