RabbitMQ--@RabbitListener及@RabbitHandle

两者区别     

   在 Spring AMQP 中,@RabbitListener 和 @RabbitHandler 是处理 RabbitMQ 消息的核心注解,但职责和使用场景完全不同。以下从 定义、区别、场景、示例 逐层解析:

一、核心定义

1. @RabbitListener

  • 作用:标记 方法或类 为 RabbitMQ 消息监听器,负责绑定队列、启动消息监听。
  • 能力
    • 可直接声明队列、交换机、绑定关系(通过 @QueueBinding)。
    • 启动后,持续监听队列,接收消息并处理。

2. @RabbitHandler

  • 作用:在 @RabbitListener 标记的类中,根据 消息 payload 的类型,将消息分发给对应的方法。
  • 能力:实现 同一队列内多种消息类型的差异化处理(类似 Java 的方法重载)。

二、关键区别

维度@RabbitListener@RabbitHandler
标记位置方法  类(类需是 Spring 组件,如 @Component只能标记方法,且必须在 @RabbitListener 标记的类中
核心职责绑定队列、启动监听,定义 “监听哪个队列”根据消息类型,分发到具体方法,定义 “如何处理该类型消息”
依赖关系可独立使用(方法级);类级使用时需配合 @RabbitHandler必须依赖 @RabbitListener(类级),无法单独使用
消息绑定通过 @QueueBinding 声明队列、交换机、路由键不涉及队列绑定,只负责类型匹配

三、使用场景对比

1. @RabbitListener 单独使用(方法级)

  • 场景:队列中的 消息类型单一(如只有 String 或单一对象),一个方法即可处理。
  • 示例
    @Component
    public class SimpleListener {// 直接监听 "simple_queue",处理 String 类型消息@RabbitListener(queues = "simple_queue")public void handleString(String message) {System.out.println("收到字符串消息: " + message);}
    }
    

2. @RabbitListener(类级) + @RabbitHandler(多方法)

  • 场景:队列中的 消息类型多样(如同时有 StringUserOrder 等),需不同方法处理。
  • 示例
    @Component
    @RabbitListener(queues = "mixed_queue") // 监听混合类型消息的队列
    public class MixedListener {// 处理 String 类型消息@RabbitHandlerpublic void handleString(String message) {System.out.println("字符串消息: " + message);}// 处理 User 类型消息(需保证生产者发送的是 User 对象,且序列化正确)@RabbitHandlerpublic void handleUser(User user) {System.out.println("用户消息: " + user.getName());}// 处理 Order 类型消息@RabbitHandlerpublic void handleOrder(Order order) {System.out.println("订单消息: " + order.getOrderId());}
    }
    

3. 高级用法:@QueueBinding 与 @RabbitListener 结合

  • 场景:监听队列时,需 动态声明队列、交换机、绑定关系(无需手动在 RabbitMQ 管理界面创建)。
  • 示例(Direct 交换机 + 路由键绑定):

    java

    @Component
    public class DirectListener {// 声明队列、交换机、绑定关系,同时监听消息@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct_queue", durable = "true"),exchange = @Exchange(name = "direct_exchange", type = ExchangeTypes.DIRECT),key = "direct_key" // 路由键))public void handleDirectMessage(String message) {System.out.println("Direct 消息: " + message);}
    }
    

四、底层原理:为什么需要两者配合?

  • @RabbitListener 负责 “连接 MQ 并订阅队列”,是消息监听的入口。
  • @RabbitHandler 负责 “消息类型分发”,解决 “同一队列内多种消息类型如何差异化处理” 的问题。

类比:@RabbitListener 是 “快递站”(订阅队列,接收包裹),@RabbitHandler 是 “分拣员”(根据包裹内容,分给不同的处理人员)。

五、避坑指南

  1. @RabbitHandler 只在类级 @RabbitListener 中生效

    • 如果 @RabbitListener 标记方法,@RabbitHandler 会被忽略。
  2. 消息类型必须匹配

    • 生产者发送的消息需正确序列化(如 JSON),消费者才能反序列化为 @RabbitHandler 方法的参数类型。
    • 若类型不匹配,会抛出 AmqpException(如 “找不到匹配的 @RabbitHandler 方法”)。
  3. 类级 @RabbitListener 需是 Spring 组件

    • 标记 @Component@Service 等,确保 Spring 扫描并创建 Bean,否则监听不生效。

总结:如何选择?

  • 简单场景(单一消息类型):直接用 方法级 @RabbitListener,无需 @RabbitHandler
  • 复杂场景(多消息类型):用 类级 @RabbitListener + 多个 @RabbitHandler,按类型分发处理。
  • 需动态声明队列 / 交换机:结合 @QueueBinding 和 @RabbitListener(方法级或类级均可)。

那要是绑定不同的消息队列呢?

        以Fanout交换机为例

先看代码的核心逻辑:每个 @RabbitListener 都绑定了独立的临时队列 

Fanout 交换机的广播特性 

 示例中,两个 @RabbitListener 的结构如下(简化分析):

@Component
public class FanoutCustomer {// 🔵 消费者1:绑定临时队列A → 交换机logs(Fanout)@RabbitListener(bindings = @QueueBinding(value = @Queue, // 临时队列A(无名称,自动生成)exchange = @Exchange(value = "logs", type = "fanout")))public void receive1(String message) { ... }// 🔵 消费者2:绑定临时队列B → 交换机logs(Fanout)@RabbitListener(bindings = @QueueBinding(value = @Queue, // 临时队列B(无名称,自动生成,与A不同)exchange = @Exchange(value = "logs", type = "fanout")))public void receive2(String message) { ... }
}

二、关键机制:每个 @RabbitListener 都会创建独立的队列

  • @Queue 无名称 → 临时队列
    Spring AMQP 会为每个 @Queue(无名称)生成 唯一的临时队列(如 amq.gen-xxx),连接关闭时自动删除。
  • 每个 @RabbitListener 绑定自己的队列
    receive1 绑定 临时队列 Areceive2 绑定 临时队列 B,两个队列相互独立。

三、Fanout 交换机的广播特性:两个队列都会收到消息

Fanout 交换机的核心是 “广播消息到所有绑定的队列”

  • 当生产者向 logs 交换机发消息时,临时队列 A 和 B 都会收到相同的消息
  • 因此,receive1 和 receive2 都会被触发,各自处理自己队列里的消息(内容相同,但属于不同队列的消费)。

四、为什么不用 @RabbitHandler?(对比场景)

以 Fanout 交换机的广播

   @RabbitHandler 的核心是 “同一队列内的消息类型分发”,而示例的场景是 “多队列的广播消费”,两者适用场景完全不同:

场景 1:多队列广播(示例中的用法)

  • 需求:让 多个消费者(队列)都收到消息(如日志系统,多个消费者分别记录日志、推送通知)。
  • 实现:每个消费者用 @RabbitListener 绑定独立队列(临时队列),借助 Fanout 交换机的广播特性,让所有队列都收到消息。

场景 2:同一队列的多类型消息(需用 @RabbitHandler

  • 需求:同一队列里有 不同类型的消息(如 StringUserOrder),需分发给不同方法处理。
  • 实现
    @Component
    @RabbitListener(queues = "mixed_queue") // 监听同一个队列
    public class MixedListener {// 处理 String 类型@RabbitHandlerpublic void handleString(String msg) { ... }// 处理 User 类型@RabbitHandlerpublic void handleUser(User user) { ... }
    }
    

五、总结:两个 @RabbitListener 的设计意图

Fanout 交换机的广播特性 

   示例中,两个 @RabbitListener 并非 “处理不同消息类型”,而是 “模拟两个独立的消费者,同时接收 Fanout 交换机的广播消息”

  • 每个 @RabbitListener 对应一个 独立的临时队列,都绑定到 Fanout 交换机。
  • Fanout 交换机将消息广播到两个队列,因此 receive1 和 receive2 都会被调用,实现 “同一消息被多个消费者处理” 的效果(如日志既存文件又推送到前端)。

扩展思考:如果要让同一队列支持多类型消息,如何改造?

只需将两个 @RabbitListener 改为 类级 @RabbitListener + @RabbitHandler

@Component
@RabbitListener(bindings = @QueueBinding(value = @Queue, // 单个临时队列exchange = @Exchange(value = "logs", type = "fanout")
))
public class FanoutCustomer {// 处理 String 消息@RabbitHandlerpublic void receive1(String message) { ... }// 处理 User 消息(假设生产者发 User 对象)@RabbitHandlerpublic void receive2(User user) { ... }
}

此时,同一个临时队列 接收 Fanout 广播的消息,@RabbitHandler 根据消息类型(String 或 User)分发到不同方法。

理解这一点后,就能区分:

  • 多队列广播 → 多个 @RabbitListener(每个绑定独立队列)。
  • 同一队列多类型 → 类级 @RabbitListener + 多个 @RabbitHandler

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90960.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90960.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【基于CKF的IMM】MATLAB例程,CV和CT两个模型下的IMM,二维,滤波使用CKF(容积卡尔曼滤波),附下载链接

本程序实现了基于交互多模型(IMM)容积卡尔曼滤波(CKF)的多模型融合定位方法,并与纯CV−CKFCV-CKFCV−CKF(匀速模型)和CT−CKFCT-CKFCT−CKF(匀角速度转弯模型)方法对比。…

AI资讯日报 - 2025年07月28日

AI资讯日报 | 2025年07月28日 周一 今日核心要点 精华提炼 技术突破 • 腾讯混元:开源发布、3D技术 • 书生Intern:开源发布、多模态 企业动态 • AI工具集:协同创作、视频生成 数据概览分类数量重点关注技术突破2 条开源发布、3D技术企业动态…

大语言模型 LLM 通过 Excel 知识库 增强日志分析,根因分析能力的技术方案(1):总体介绍

文章大纲 1. 核心目标 2. 系统总体架构 3. Google Cloud 端到端方案(含无 RAG & RAG 双模式) 3.1 无 RAG:Function-Calling 查表模式 3.2 RAG:托管式向量检索 4. 开源轻量级方案 5. 数字孪生联合验证(实验性) 6. 知识图谱增强(Neo4j) 7. 监控与持续优化(CometLLM)…

Deepseek + browser-use 轻松实现浏览器自动化

在数字化时代,浏览器应用广泛,浏览器自动化可大幅提升效率。Deepseek 是强大的智能语言模型,能精准解析复杂指令,browser - use 是专注浏览器操作的工具,提供丰富 API 接口,支持主流浏览器的各类自动化操作…

开疆智能ModbusTCP转Profient网关连接西门子PLC与川崎机器人配置案例

本案例是西门子PLC与川崎机器人通过Profient转ModbusTCP网关进行通讯转换的配置案例,西门子作为profinet主站,机器人作为ModbusTCP服务器。配置过程:机器人配置川崎机器人控制器提供了RS232、以太网的通信接口,同时也可通过加装选…

Docker多主机网络连接:实现跨主机通信

Docker 是一种流行的容器化平台,它可以帮助开发人员更方便地构建、发布和管理应用程序。在 Docker 中,容器是独立运行的应用程序包装,包含了运行所需的所有文件、库和环境变量。Docker 提供了多种网络连接方式,使得容器之间可以进…

OSPF笔记

一、OSPF基础1、技术背景(RIP中存在的问题)RIP中存在最大跳数为15的限制,不能适应大规模组网周期性发送全部路由信息,占用大量的带宽资源路由收敛速度慢以跳数作为度量值存在路由环路可能性每隔30秒更新2、OSPF协议特点没有跳数限…

kotlin基础【3】

Kotlin Playground: Edit, Run, Share Kotlin Code Online 资料&#xff1a;kotlin实战 第一章 data class Person(val name: String,val age:Int?null)//允许接受以age为空&#xff0c;当为空将它赋值为null,如果不这么写直接写age:Int?是否可以fun main(args:Array<St…

Java-数构二叉树

1.树 1.1概念 树是一种非线性的数据结构&#xff0c;它是由n个有限节点组成一个具有层次关系。这种结构有以下特点&#xff1a; 一个特殊的结点&#xff0c;称为根节点&#xff0c;根节点没有前驱节点除根节点以外&#xff0c;其余节点分成M个互不相交的集合。每个集合又是一…

编程中水合的理解

在编程中&#xff0c;水合&#xff08;Hydration&#xff09; 是一个常见概念&#xff0c;尤其在 前端开发 和 服务端渲染&#xff08;SSR&#xff09; 场景中频繁出现。它的核心含义是&#xff1a;将静态内容“激活”为交互式动态内容。1. 水合的本质简单理解&#xff1a;水合…

使用ffmpeg转码h265后mac默认播放器不支持问题

由于mac自带录屏是mov并且文件特别大&#xff0c;我使用ffmpeg转码视频为h265使用如下命令ffmpeg_command [ffmpeg_path,"-i", input_path,"-c:v", "libx265","-preset", "veryslow","-map_metadata", "0&q…

支持MySQL、PostgreSQL和Redis集群部署,1Panel开源面板v2.0.5版本发布

2025年7月24日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v2.0.5版本。在这一版本中&#xff0c;1Panel新增数据库集群部署、邮件告警和主从节点灵活切换三项功能&#xff0c;聚焦为企业级运维场景提供更优使用体验。 1Panel v2.0.5版本是1Panel开源面板…

GaussDB 数据库架构师修炼(九) 逻辑备份实操

1 逻辑备份定义 逻辑备份是指与业务有关的对象进行备份&#xff0c;这个对象包括表、表的数据、视图、索引、过程、函数等等。GaussDB支持逻辑备份的工具为gs_dump、gs_restore&#xff0c;以下举例说明。 2 创建举例数据 以下创建testdb库&#xff0c;创建test1模式&#xf…

c# Winform发布成独立文件

改造前&#xff1a; 通过发布页面&#xff0c;修改部署模式为独立&#xff0c;输出文件目录没有完全包含所有dll改造后&#xff1a;通过修改项目文件方式修改csproj前&#xff1a;<PropertyGroup><OutputType>WinExe</OutputType><TargetFramework>net…

Android基础(一) 运行HelloWorld

Android基础&#xff08;一&#xff09; 运行HelloWorld一、创建你的第一个Android项目二、创建HelloWorld项目三、安装并启动模拟器四、安装三方模拟器五、使用真机一、创建你的第一个Android项目 学习任何一门编程语言&#xff0c;编写的第一个程序都是Hello World&#xff0…

MongoDB 和 Elasticsearch(ES)区别

MongoDB 和 Elasticsearch&#xff08;ES&#xff09;都是流行的 NoSQL 数据库&#xff0c;但设计目标和适用场景有显著区别。以下是它们的核心差异和典型使用场景对比&#xff1a;1. 核心定位特性MongoDBElasticsearch数据库类型文档数据库&#xff08;通用型 OLTP&#xff09…

【C++算法】89.多源BFS_01 矩阵

文章目录题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;题目链接&#xff1a; 542. 01 矩阵 题目描述&#xff1a; 解法 先看懂题目 解法一&#xff1a;一个位置一个位置求&#xff08;最差的情况下会非常恐怖&#xff09; 解法二&#xff1a;多源BFS正…

数据结构之 【排序】(归并排序)

目录 1.递归实现归并排序的思想及图解 2.递归实现归并排序的代码逻辑 2.1嵌套子函数 2.2递归过程 2.3递归结束条件 2.4归并及拷贝过程 3.非递归实现归并排序的思想及图解 4.非递归实现归并排序的代码逻辑 4.1边归并边拷贝 4.2某一gap下归并完成才进行拷贝 5.归并排…

企业如何选择适合的高防服务器?

高防服务器租用哪家好&#xff1f;这个问题困扰着许多站长&#xff0c;建立的网站经常受到各种网络攻击&#xff0c;虽然高防服务器有着较高的防御性能&#xff0c;十分适合经常被攻击的行业网站&#xff0c;但是如何租到满意的高防服务器呢&#xff01;徐州高防服务器是部署在…

告别重复劳动:Ansible 自动化运维超详细学习路线图

在运维的世界里&#xff0c;我们总是在与重复性任务作斗争&#xff1a;部署同一套环境 N 次、在几十台服务器上修改同一个配置文件、一遍又一遍地执行相同的发布流程……这些工作不仅枯燥&#xff0c;还极易出错。 如果你也为此感到烦恼&#xff0c;那么 Ansible 就是为你量身打…