如何在Spring Boot中使用Redis发布订阅功能_配置RedisMessageListenerContainer

作者:袖梨 2026-07-11
RedisMessageListenerContainer 必须手动声明 Bean,Spring Boot 2.0+ 不自动配置;监听器需实现 MessageListener 并通过 addMessageListener() 注册,不可用 @EventListener;序列化需与发布端一致,推荐统一使用 StringRedisTemplate。

RedisMessageListenerContainer 配置必须手动声明 Bean

Spring Boot 2.0+ 默认不自动配置 RedisMessageListenerContainer,哪怕你引入了 spring-boot-starter-data-redis 并配好了 RedisConnectionFactory。不显式定义这个 Bean,@EventListener 或监听器类根本不会被注册,订阅行为静默失效。

常见错误现象:RedisMessageListenerContainer 没有启动日志(如 “Starting RedisMessageListenerContainer”),发布消息后监听器无任何响应,也没有报错。

正确做法是手动声明 Bean,并确保它依赖已配置的 RedisConnectionFactory

@Beanpublic RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {    RedisMessageListenerContainer container = new RedisMessageListenerContainer();    container.setConnectionFactory(connectionFactory);    container.setTaskExecutor(new ThreadPoolTaskExecutor()); // 避免阻塞主线程    return container;}

监听器注册要用 addMessageListener(),不是 @EventListener

Spring 的 @EventListener 只适用于 Spring 事件(ApplicationEvent),对 Redis 发布/订阅消息完全无效。必须通过 RedisMessageListenerContainer.addMessageListener() 显式注册监听器实例。

典型误区:写一个带 @Component@EventListener 的类,以为能收到 Redis 消息——结果永远收不到。

实操建议:

  • 监听器需实现 MessageListener 接口(或继承 KeyExpirationEventMessageListener 等子类)
  • PatternTopic 订阅通配符频道(如 PatternTopic("user:*")),用 ChannelTopic 订阅精确频道(如 ChannelTopic("user.login")
  • 注册时传入监听器实例和 Topic 列表,例如:container.addMessageListener(myListener, new ChannelTopic("order.created"))

消息序列化问题常导致 onMessage() 收到 null 或乱码

默认 RedisMessageListenerContainer 使用 JdkSerializationRedisSerializer,但如果你的生产者用的是 StringRedisTemplate(即 StringRedisSerializer),两边序列化方式不一致,onMessage(Message, byte[]) 中的 message.getBody() 会是乱码或空字节数组。

解决方案取决于你的使用场景:

  • 统一用 StringRedisTemplate:配置容器时设置 setTopicSerializer(new StringRedisSerializer())(注意:该方法仅在 Spring Data Redis 2.6+ 可用)
  • 降级兼容老版本:自定义 MessageListener,在 onMessage() 内手动用 new String(message.getBody(), StandardCharsets.UTF_8) 解码
  • 避免混用:不要让 RedisTemplateStringRedisTemplate 对同一组频道收发消息

发布端必须用 RedisTemplate 或 StringRedisTemplate 的 convertAndSend()

不能用 execute() + 原生 PUBLISH 命令绕过序列化逻辑,否则监听器收到的消息体无法被反序列化(尤其当容器配置了非默认序列化器时)。

正确发布方式只有两个:

  • redisTemplate.convertAndSend("channel.name", "hello") —— 要求 redisTemplate 的 value serializer 与监听器解码方式匹配
  • stringRedisTemplate.convertAndSend("channel.name", "hello") —— 最简单,推荐用于纯字符串消息

容易忽略的一点:convertAndSend() 是异步非阻塞的,但若连接池耗尽或 Redis 不可用,它会抛出 RedisConnectionFailureException,需在外层捕获并处理,而不是假设“发了就一定到”。

相关文章

精彩推荐