用 Spring Boot 与 RabbitMQ 完成消息发送和接收

未完纪 · 编程开发 · WWJ-374

一个发送端、一个主题交换机、一条队列和一个普通 Java 接收对象,就能串起 Spring AMQP 的基本消息链路。本文从本地 RabbitMQ 服务开始,完整走过项目初始化、监听器注册、发送测试消息和打包运行。

原作者:Spring 团队(原页未列个人署名)
原文:Messaging with RabbitMQ · 中文翻译与技术注:未完纪 · 核验日期:2026-10-05

要构建什么,需要准备什么

我们要构建一个 Spring Boot 应用,让它向 RabbitMQ AMQP 服务发布消息,再订阅并处理消息。发送侧使用 Spring AMQP 的 RabbitTemplate,接收侧用 MessageListenerAdapter 把消息交给一个 POJO,也就是普通 Java 对象。

原指南估计需要约 15 分钟。准备一个编辑器或 IDE,以及 Java 17 或更高版本。若采用本文默认的 Spring Boot Docker Compose 集成,还需要可用的 Docker 环境,例如 Docker Desktop。项目构建可选择 Maven 或 Gradle。

你可以从零跟随步骤,也可以直接查看示例仓库中的完整解法。获取代码有三种方式:下载仓库 ZIP 后解压;用 Git 克隆;或 Fork 仓库,以便通过 Pull Request 向原指南贡献修改。克隆命令如下:

git clone https://github.com/spring-guides/gs-messaging-rabbitmq.git

版本注:Java 17 是原指南给出的下限,并非任意未来 Spring Boot 版本的保证。构建插件、依赖版本和 Gradle/Maven Wrapper 以下载项目中的构建文件为准。本文不把会持续更新的仓库或 Initializr 输出视为固定版本。

先准备 RabbitMQ 服务

消息应用需要一个 broker 来接收、路由和投递消息。本文默认使用 Spring Boot 的 Docker Compose 支持。将 spring-boot-docker-compose 加入依赖后,Spring Boot 会在工作目录中查找 compose.yml 等常见 Compose 文件名,调用 docker compose up,为受支持的容器创建服务连接 Bean,并在应用关闭时调用 docker compose stop。按照后面的项目初始化步骤选择相应依赖,即可采用这种方式。

也可以自行管理 RabbitMQ:从 RabbitMQ 官网 下载服务器并手动运行;在 macOS 上通过 Homebrew 安装;或者直接用 docker-compose up 运行 compose.yaml。选择这些方式时,应从 Maven 或 Gradle 构建文件中移除 spring-boot-docker-compose 依赖,再按后文“打包前:连接外部 RabbitMQ”的方式配置 application.properties。默认的自动 Compose 路径暂时不需要补这份连接配置。

运行方式注:Docker Compose 集成主要用于开发阶段。当前 Spring Boot 文档说明,已经运行的 Compose 服务不会由它重复启动,应用退出时也不会代为停止;可执行归档默认不包含该开发依赖。因此,开发时的自动发现连接不能直接当作独立 JAR 或容器的部署方案。原文使用的 docker-compose 是兼容写法,现代 Compose 环境通常使用 docker compose;请按本机实际安装方式选择。

用 Spring Initializr 创建项目

打开 Spring Initializr,选择 Maven 或 Gradle,以及 Java 语言。在 Dependencies 中加入 Spring for RabbitMQ 和 Docker Compose Support,点击 Generate,下载并解压生成的 ZIP。Initializr 会根据这些选择准备依赖和项目结构。若 IDE 集成了 Spring Initializr,也可以直接在 IDE 内完成这些步骤。原指南同时提供了原文中的预选项目入口;该服务的可用版本会变化,应在 Initializr 中选择受支持版本并保留上述依赖。

Runner 使用 RabbitTemplate 将 foo.bar.baz 消息发给 spring-boot-exchange,交换机以 foo.bar.# 绑定路由至 spring-boot 队列,再经监听容器和适配器调用 Receiver.receiveMessage
图 1:本文示例的一次消息流。原创结构示意图,依据原文代码绘制;不代表生产部署拓扑。

创建消息接收器

先创建一个能够响应消息的方法。文件路径为 src/main/java/com/example/messagingrabbitmq/Receiver.java:

package com.example.messagingrabbitmq;

import java.util.concurrent.CountDownLatch;
import org.springframework.stereotype.Component;

@Component
public class Receiver {

  private CountDownLatch latch = new CountDownLatch(1);
  public void receiveMessage(String message) {
    System.out.println("Received <" + message + ">");
    latch.countDown();
  }

  public CountDownLatch getLatch() {
    return latch;
  }

}

Receiver 是一个 POJO,receiveMessage() 就是接收入口。方法名不必固定为这个名字,只要注册接收器时告诉适配器应调用哪个方法即可。

CountDownLatch 用于向主流程发出“消息已经收到”的信号:初始计数是 1,收到消息后调用 countDown(),等待方就能继续。这是为了让简短示例在退出前等待一次接收,生产消息服务通常不会用这种方式管理生命周期。

注册监听器、队列、交换机和绑定

RabbitTemplate 提供 RabbitMQ 消息收发能力,但还需要配置消息监听容器,声明队列、交换机及两者之间的绑定,再准备一个用于发送测试消息的组件。Spring Boot 会自动创建连接工厂和 RabbitTemplate,因此无需手写这些基础对象。

发送端和消息监听容器都通过连接工厂连接 RabbitMQ。将下面的应用类保存到 src/main/java/com/example/messagingrabbitmq/MessagingRabbitmqApplication.java:

package com.example.messagingrabbitmq;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MessagingRabbitmqApplication {

  static final String topicExchangeName = "spring-boot-exchange";

  static final String queueName = "spring-boot";

  @Bean
  Queue queue() {
    return new Queue(queueName, false);
  }

  @Bean
  TopicExchange exchange() {
    return new TopicExchange(topicExchangeName);
  }

  @Bean
  Binding binding(Queue queue, TopicExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#");
  }

  @Bean
  SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
      MessageListenerAdapter listenerAdapter) {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.setQueueNames(queueName);
    container.setMessageListener(listenerAdapter);
    return container;
  }

  @Bean
  MessageListenerAdapter listenerAdapter(Receiver receiver) {
    return new MessageListenerAdapter(receiver, "receiveMessage");
  }

  public static void main(String[] args) throws InterruptedException {
    SpringApplication.run(MessagingRabbitmqApplication.class, args).close();
  }

}

@SpringBootApplication 的具体作用可查阅 Spring Boot 参考文档。在本例中,listenerAdapter() 把 Receiver 包装成监听器,并指定调用 receiveMessage;container() 再把这个监听器注册到容器,让它监听名为 spring-boot 的队列。

queue() 创建 AMQP 队列,exchange() 创建主题交换机,binding() 则描述交换机应该把哪些消息路由到这条队列。按照原指南的配置方式,Queue、TopicExchange 和 Binding 都要作为独立的顶层 Spring Bean 声明,才能正确完成资源声明。

AMQP 与 JMS 的队列语义有需要区分的地方。队列中的一条消息会交给某个消费者,但 AMQP 生产者把消息发送给交换机,再由交换机决定将它路由到一条或多条队列。后者也可以实现类似 JMS topic 的广播效果。这个示例只有一条绑定队列,因此它展示的是发布、路由、接收的基本过程,而不是多个独立订阅者的完整广播场景。

这里的绑定键是 foo.bar.#,发送时采用的路由键只要符合该模式,就会进入 spring-boot 队列。原文用“以 foo.bar. 开头”作直观说明;更精确地说,主题键按点分隔成单词,# 可以匹配零个或多个单词,因此 foo.bar 本身也能匹配,foo.bar.baz 当然也能匹配。参见 RabbitMQ 官方 Topics 教程。

可靠性注:new Queue(queueName, false) 明确创建非持久队列。此配置不保证 broker 重启后队列仍存在。示例也没有展示发布确认、退回处理、业务幂等、重试、死信或生产账号隔离;“发送调用返回”不等于已获得业务处理成功的端到端保证。

发送一条测试消息

测试消息由 CommandLineRunner 发送。它在应用启动后运行,发送消息,然后等待接收器中的 latch。文件路径为 src/main/java/com/example/messagingrabbitmq/Runner.java:

package com.example.messagingrabbitmq;

import java.util.concurrent.TimeUnit;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class Runner implements CommandLineRunner {

  private final RabbitTemplate rabbitTemplate;
  private final Receiver receiver;

  public Runner(Receiver receiver, RabbitTemplate rabbitTemplate) {
    this.receiver = receiver;
    this.rabbitTemplate = rabbitTemplate;
  }

  @Override
  public void run(String... args) throws Exception {
    System.out.println("Sending message...");
    rabbitTemplate.convertAndSend(MessagingRabbitmqApplication.topicExchangeName, "foo.bar.baz", "Hello from RabbitMQ!");
    receiver.getLatch().await(10000, TimeUnit.MILLISECONDS);
  }

}

convertAndSend() 的三个参数依次是交换机名称、路由键和消息内容。foo.bar.baz 与前面的绑定模式匹配。测试时可以模拟(mock)这个 runner,从而单独测试接收器。

静态审查注:await(10000, TimeUnit.MILLISECONDS) 最多等待 10 秒,代码没有检查其布尔返回值。超时后 runner 也会结束,随后 main() 关闭应用上下文;所以进程退出本身不能作为已收到消息的判据。另外,原文对 Runner 的路径曾写成 src/main/java/com.example.messagingrabbitmq/Runner.java,本文按 Java 包目录写成斜杠路径,包声明与代码未改。

运行应用

main() 先创建 Spring 应用上下文,监听容器随之启动并开始监听。随后自动执行 Runner Bean,使用容器中的 RabbitTemplate 发布 Hello from RabbitMQ!。消息经交换机到达队列,接收器打印内容并释放 latch,最后应用上下文关闭,进程退出。

可以直接从 IDE 运行 main 方法。若克隆的是完整示例仓库,IDE 的工作目录有时不是 compose.yaml 所在目录;此时应调整工作目录或 Compose 文件路径。也可以在实际应用模块目录使用项目自带的构建 Wrapper:

./gradlew bootRun
# 或使用 Maven
./mvnw spring-boot:run

原指南指出,采用这些命令可以让应用找到对应的 Compose 文件。Windows PowerShell 用户应使用项目中的 .\gradlew.bat 或 .\mvnw.cmd,具体入口以仓库文件为准;上述原文命令按类 Unix shell 展示。

打包前:连接外部 RabbitMQ

如果不使用 Spring Boot Docker Compose 集成,就需要一个已经运行的 RabbitMQ 实例。继续使用 Compose 时,原指南要求把端口映射改成 '5672:5672',并增加容器名。完整 compose.yaml 为:

services:
  rabbitmq:
    container_name: 'guide-rabbit'
    image: 'rabbitmq:latest'
    environment:
      - 'RABBITMQ_DEFAULT_PASS=secret'
      - 'RABBITMQ_DEFAULT_USER=myuser'
    ports:
      - '5672:5672'

然后用 docker-compose up 启动服务。因为连接信息不再由 Spring Boot 的开发服务集成自动提供,还需要在 src/main/resources/application.properties 中加入:

spring.rabbitmq.password=secret
spring.rabbitmq.username=myuser

部署与安全注:secret 和 myuser 是公开的演示凭据,不能原样用于可访问的生产服务;rabbitmq:latest 会随时间变化,不适合可复现部署;5672:5672 默认可能向宿主机多个接口发布端口。仅本机练习时,可将端口映射限定为 127.0.0.1:5672:5672,并为实际环境另行设置版本固定、网络访问控制、凭据管理和 TLS。这里没有配置数据卷,也没有覆盖 broker 数据持久化方案。若服务不在默认本机地址与端口,还要配置对应 host、port 和 virtual host;不要只修改密码便假定连接目标正确。

构建 JAR 或容器镜像

原指南提供两条路径:构建并执行可执行 JAR,以及使用 Cloud Native Buildpacks 构建容器镜像。两种方式都应得到同样的消息输出。

使用 Gradle 打包和运行 JAR:

./gradlew clean build
java -jar build/libs/messaging-rabbitmq-0.0.1-SNAPSHOT.jar

如果已有 Docker 环境,也可以直接通过 Maven 或 Gradle 的 Spring Boot 插件使用 Buildpacks 构建镜像。原文给出的 Gradle 命令及运行命令如下:

./gradlew bootBuildImage
docker run --network container:guide-rabbit docker.io/library/messaging-rabbitmq:0.0.1-SNAPSHOT

--network container:guide-rabbit 让应用容器共享已经运行的 guide-rabbit 容器的网络命名空间,具体行为见 Docker 网络文档。这解释了为什么此路径前面要给 RabbitMQ 容器固定名称。文件名和镜像标签取决于项目构建设置;若修改了项目名或版本号,请同步调整命令中的名字。

按照原文,无论采用哪一种构建运行方式,都应能看到下面两行核心输出;实际启动日志通常还包括框架信息:

Sending message...
Received <Hello from RabbitMQ!>

接下来可以继续学什么

至此,一个简单的 Spring 与 RabbitMQ 发布和订阅应用就建立起来了:发送者把消息交给交换机,绑定规则选择队列,监听容器再把消息转交给 POJO。更多功能可继续阅读 Spring AMQP 参考文档与 Spring AMQP Samples。

原指南的相关教程还包括:Messaging with Redis、Messaging with JMS 和 Building an Application with Spring Boot。若希望编写新指南或改进现有内容,可查看 贡献说明。源页也提供 Spring Academy 云端学习入口。

来源与许可:本文依据 Spring 团队的 Messaging with RabbitMQ 完整翻译整理。原页将示例代码标注为 Apache License 2.0(许可全文),文字标注为 CC BY-ND 3.0;原站版权声明为 Copyright © 2005–2026 Broadcom,保留所有权利。不将原文字许可解释为允许任意翻译分发,也不代表 Spring 或 Broadcom 背书。中文标题、段落组织、原创图和明确标为“注”的说明属于本次编辑加工;示例代码逻辑保留原样。

版权与许可全文

以下保留本页涉及的来源材料或示例代码的版权、许可条件与免责声明;各自适用范围依原声明。中文翻译及编辑标注:未完纪,2026-10-05。

Apache-2.0

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容