Java-209 Spring AMQP 整合 RabbitMQ 实战:XML 配置直连交换机、RabbitAdmin 自动声明与收发闭环

TL;DR

-场景:Spring 5.x 用 Spring AMQP(spring-rabbit)接入 RabbitMQ,XML 声明队列/交换机/绑定并完成收发闭环。

  • 结论:RabbitAdmin 负责资源自动声明;RabbitTemplate/AmqpTemplate 负责收发;直连交换机需显式指定 exchange+routingKey 避免默认交换机误投。
  • 产出:一套可运行的 rabbit-context.xml + 最小闭环 StartApp 示例,并配套常见错误定位与修复速查。

版本矩阵

组件版本/配置 已验证 说明
JDK 1.8(maven.compiler.source/target=8) 与示例代码一致
Spring Context 5.2.6.RELEASE POM 明确指定
spring-rabbit(Spring AMQP)2.2.7.RELEASE POM 明确指定
Spring AOP 5.2.6.RELEASE ⚠️ 文中标注"注解驱动需要",但当前示例走 XML + 直接取 Bean,不依赖 AOP
RabbitMQ Server 与 exchange/queue durable、权限、vhost 强相关;缺版本信息无法标记已验证

Spring 整合 RabbitMQ

基本介绍

Spring AMQP 是 Spring 框架对 AMQP (Advanced Message Queuing Protocol) 协议的抽象实现,它提供了统一的编程模型来操作 AMQP。而 Spring Rabbit 是 Spring AMQP 的具体实现,专门针对 RabbitMQ 消息中间件进行了封装和扩展。

Spring AMQP/Rabbit 的核心组件包括:

  1. RabbitAdmin
    用于声明和管理 RabbitMQ 资源(队列、交换器、绑定等)的组件。当应用启动时,RabbitAdmin 会自动检测 Spring 容器中所有的 Queue、Exchange 和 Binding 对象,并在 RabbitMQ 服务器上创建相应的资源。例如:
java 复制代码
   @Bean
   public Queue myQueue() {
       return new Queue("my.queue");
   }
   
   @Bean
   public RabbitAdmin admin(ConnectionFactory connectionFactory) {
       return new RabbitAdmin(connectionFactory);
   }
  1. RabbitTemplate
    是发送和接收消息的核心工具类,封装了与 RabbitMQ 交互的基本操作。它提供了多种消息发送方法(如 convertAndSend、send 等)和接收方法(如 receive、receiveAndConvert 等)。典型用法:
java 复制代码
   rabbitTemplate.convertAndSend("exchange.name", "routing.key", messageObject);
   Object received = rabbitTemplate.receiveAndConvert("queue.name");
  1. SimpleMessageListenerContainer
    消息监听容器,用于异步消费消息。它可以配置多个监听器来消费不同队列的消息,支持并发消费、消息确认等特性。配置示例:
java 复制代码
   @Bean
   public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory) {
       SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
       container.setConnectionFactory(connectionFactory);
       container.setQueueNames("my.queue");
       container.setMessageListener(message -> {
           // 处理消息逻辑
       });
       container.setConcurrentConsumers(5); // 设置并发消费者数量
       return container;
   }

这些核心组件协同工作,为开发者提供了完整的消息收发解决方案。RabbitTemplate 负责同步消息操作,SimpleMessageListenerContainer 处理异步消费,RabbitAdmin 则管理基础资源,共同构成了 Spring RabbitMQ 应用的基础架构。

RabbitAdmin类完成对 Exchange、Queue、Binding 的操作,在容器中管理了 RabbitAdmin 类的时候,可以对 Exchange、Queue、Binding进行自动声明。

RabbitTemplate 类是发送和接收消息的工具类。

SimpleMessageListenerContainer 是消费消息的容器。

整合项目

POM

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>icu.wzk</groupId>
    <artifactId>rabbitmq-demo-spring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>
        <!-- 可选:注解驱动需要 AOP -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
    </dependencies>
</project>

基于XML

rabbit-context.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/rabbit
         http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <rabbit:connection-factory id="connectionFactory"
                               host="localhost"
                               port="5672"
                               virtual-host="/"
                               username="admin"
                               password="secret" />

    <!-- RabbitTemplate:用于发送/接收消息 -->
    <rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>

    <!-- RabbitAdmin:自动声明 Queue / Exchange / Binding -->
    <rabbit:admin id="rabbitAdmin" connection-factory="connectionFactory"/>

    <!-- 队列 -->
    <rabbit:queue name="myqueue"/>

    <!-- 直连交换机 + 绑定 -->
    <rabbit:direct-exchange name="direct.biz.ex"
                            auto-declare="true"
                            auto-delete="false"
                            durable="false">
        <rabbit:bindings>
            <rabbit:binding queue="myqueue" key="dir.ex"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>

</beans>

编写代码

java 复制代码
package icu.wzk;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 最小闭环示例:启动 Spring 容器 -> 发送消息 -> 从队列取一条消息 -> 关闭容器
 *
 * 依赖前提(与 rabbit-context.xml 保持一致):
 * - 存在队列:myqueue
 * - 存在绑定:myqueue <- (direct.biz.ex, routingKey=dir.ex)
 *
 * 注意:
 * 1) convertAndSend(String routingKey, Object message) 这个重载使用的是"默认交换机"(amq.default)。
 *    默认交换机的路由规则:routingKey 必须等于队列名,否则投递不到队列。
 * 2) 你现在 convertAndSend("dir.ex", ...) 会把 "dir.ex" 当成队列名路由;
 *    如果你的队列不是叫 dir.ex,而是 myqueue,那么应该显式指定 exchange + routingKey:
 *    convertAndSend("direct.biz.ex", "dir.ex", ...)
 * 3) receiveAndConvert("myqueue") 是同步拉取(basicGet),空队列会返回 null。
 */
public class StartApp {

    // 与 XML 中定义保持一致:交换机 / 路由键 / 队列
    private static final String EXCHANGE = "direct.biz.ex";
    private static final String ROUTING_KEY = "dir.ex";
    private static final String QUEUE = "myqueue";

    public static void main(String[] args) {
        // 1) 加载 Spring XML,初始化连接工厂、RabbitTemplate、RabbitAdmin、队列/交换机/绑定
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("rabbit-context.xml");

        // 2) 获取发送/接收入口(底层通常是 RabbitTemplate)
        AmqpTemplate amqp = context.getBean(AmqpTemplate.class);

        // 3) 批量发送 1000 条消息到指定 exchange + routingKey
        //    这样不依赖默认交换机的"routingKey==队列名"规则,路由更清晰
        for (int i = 0; i < 1000; i++) {
            amqp.convertAndSend(EXCHANGE, ROUTING_KEY, "wzk" + i);
        }

        // 4) 同步拉取队列中的一条消息(可能为 null:队列为空 / 尚未投递完成 / 被其他消费者取走)
        Object msg = amqp.receiveAndConvert(QUEUE);
        System.out.println(msg);

        // 5) 关闭容器:释放连接等资源
        context.close();
    }
}

编写代码如下所示:

启动MQ之后,我们运行程序即可。

测试运行

我们测试运行可以看到:

错误速查

症状 根因定位 修复
启动报 Connection refused / 连接超时 RabbitMQ 未启动、端口/Host 不通、防火墙拦截 telnet host 5672 / RabbitMQ 日志 / 容器端口映射 启动 RabbitMQ;确认 host=localhost 是否在同一台机器;放通 5672
启动报 ACCESS_REFUSED - Login was refused 用户名/密码错误或无 vhost 权限 RabbitMQ 管理台 Users / vhost 权限;应用日志 修正 username/password;给该用户授予 vhost / 的 configure/write/read
启动报 NOT_FOUND - no exchange 'direct.biz.ex' 交换机未声明成功或声明顺序/权限问题 RabbitMQ 管理台 Exchanges 是否存在;RabbitAdmin 日志 确保 <rabbit:admin .../> 存在且连接正常;确保用户具备 configure 权限
发送无异常但队列无消息 使用了 convertAndSend(routingKey, msg) 走默认交换机,routingKey≠队列名导致无法路由 开启 publisher-returns / RabbitMQ 管理台看队列 Ready=0;对照调用重载 使用 convertAndSend(exchange, routingKey, msg);或让 routingKey 等于队列名(仅默认交换机规则)
receiveAndConvert("myqueue") 返回 null 队列为空/尚未投递完成/被其他消费者取走 RabbitMQ 管理台队列消息数;是否存在监听容器 确认路由是否命中;临时停掉其他消费者;必要时增加等待/改用监听消费
启动报 PRECONDITION_FAILED - inequivalent arg 'durable' ... 同名 exchange/queue 已存在但属性不同(durable/autoDelete 等不一致) RabbitMQ 管理台查看该资源属性;报错堆栈包含 "inequivalent arg" 统一 durable/auto-delete 配置;或删除旧资源后再启动(注意生产风险)
Spring XML 解析报 schema/DTD 相关错误 无法加载 spring-rabbit.xsd(网络/代理/离线)或 schemaLocation 不匹配 堆栈包含 schema 下载失败/解析失败 保证可访问 schema;或使用本地 catalog;核对 xsi:schemaLocation 与版本
direct 交换机绑定了但仍不路由 routingKey 不精确匹配;绑定 key 写错;队列名写错 管理台 Bindings;发送时 routingKey 打印/对照 direct 必须精确匹配;统一 ROUTING_KEY 常量与 XML key
消费端并发/确认异常(后续扩展) 使用 SimpleMessageListenerContainer 时 ack、prefetch、并发不当 监听容器日志、消费堆栈、队列积压变化 配置 manual ack / prefetch / concurrency;结合业务幂等

其他系列

🚀 AI篇持续更新中(长期更新)

AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究 ,持续打造实用AI工具指南!
AI研究-132 Java 生态前沿 2025:Spring、Quarkus、GraalVM、CRaC 与云原生落地
🔗 AI模块直达链接

💻 Java篇持续更新中(长期更新)

Java-207 RabbitMQ Direct 交换器路由:RoutingKey 精确匹配、队列多绑定与日志分流实战

MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务已完结,Dubbo已完结,MySQL已完结,MongoDB已完结,Neo4j已完结,FastDFS 已完结,OSS已完结,GuavaCache已完结,EVCache已完结,RabbitMQ正在更新... 深入浅出助你打牢基础!
🔗 Java模块直达链接

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
🔗 大数据模块直达链接

相关推荐
崎岖Qiu3 小时前
【设计模式笔记19】:建造者模式
java·笔记·设计模式·建造者模式
⑩-6 小时前
SpringCloud-Sleuth链路追踪实战
后端·spring·spring cloud
SUPER52666 小时前
本地开发环境_spring-ai项目启动异常
java·人工智能·spring
moxiaoran57536 小时前
Spring AOP开发的使用场景
java·后端·spring
小王师傅6611 小时前
【轻松入门SpringBoot】actuator健康检查(上)
java·spring boot·后端
醒过来摸鱼11 小时前
Java classloader
java·开发语言·python
专注于大数据技术栈11 小时前
java学习--StringBuilder
java·学习
loosenivy11 小时前
企业银行账户归属地查询接口如何用Java调用
java·企业银行账户归属地·企业账户查询接口·企业银行账户查询
IT 行者11 小时前
Spring Security 6.x 迁移到 7.0 的完整步骤
java·spring·oauth2