Overview
This article introduces Spring 5.x using Spring AMQP (spring-rabbit) to integrate with RabbitMQ, declaring queues/exchanges/bindings through XML configuration, and completing send/receive loop.
Core Points:
- RabbitAdmin负责资源自动声明
- RabbitTemplate/AmqpTemplate负责收发
- Direct exchange needs to explicitly specify exchange + routingKey to avoid default exchange misdelivery
Core Components
1. RabbitAdmin
Used to declare and manage RabbitMQ resources (queues, exchanges, bindings, etc.). When application starts, RabbitAdmin automatically detects all Queue, Exchange and Binding objects in Spring container, and creates corresponding resources on RabbitMQ server.
2. RabbitTemplate
Core utility class for sending and receiving messages. Provides multiple message sending methods (like convertAndSend, send) and receiving methods (like receive, receiveAndConvert).
3. SimpleMessageListenerContainer
Message listener container for async message consumption. Supports concurrent consumption, message acknowledgment and other features.
POM Configuration
<?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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
</project>
XML Configuration (rabbit-context.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: for sending/receiving messages -->
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>
<!-- RabbitAdmin: auto-declare Queue / Exchange / Binding -->
<rabbit:admin id="rabbitAdmin" connection-factory="connectionFactory"/>
<!-- Queue -->
<rabbit:queue name="myqueue"/>
<!-- Direct exchange + binding -->
<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 Code (StartApp.java)
package icu.wzk;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Minimal loop example: Start Spring container -> Send message -> Pull one message from queue -> Close container
*
* Dependency prerequisites (consistent with rabbit-context.xml):
* - Queue exists: myqueue
* - Binding exists: myqueue <- (direct.biz.ex, routingKey=dir.ex)
*
* Note:
* 1) convertAndSend(String routingKey, Object message) this overload uses "default exchange" (amq.default).
* Default exchange routing rules: routingKey must equal queue name, otherwise delivery fails.
* 2) You now convertAndSend("dir.ex", ...) treats "dir.ex" as queue name for routing;
* If your queue is not called dir.ex but myqueue, then should explicitly specify exchange + routingKey:
* convertAndSend("direct.biz.ex", "dir.ex", ...)
* 3) receiveAndConvert("myqueue") is synchronous pull (basicGet), empty queue returns null.
*/
public class StartApp {
// Consistent with XML definition: exchange / routing key / queue
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) Load Spring XML, initialize connection factory, RabbitTemplate, RabbitAdmin, queue/exchange/binding
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("rabbit-context.xml");
// 2) Get send/receive entry (underlying is usually RabbitTemplate)
AmqpTemplate amqp = context.getBean(AmqpTemplate.class);
// 3) Batch send 1000 messages to specified exchange + routingKey
// This way doesn't rely on default exchange's "routingKey==queue name" rule, routing clearer
for (int i = 0; i < 1000; i++) {
amqp.convertAndSend(EXCHANGE, ROUTING_KEY, "wzk" + i);
}
// 4) Synchronously pull one message from queue (may be null: queue empty / not yet delivered / taken by other consumer)
Object msg = amqp.receiveAndConvert(QUEUE);
System.out.println(msg);
// 5) Close container: release connection and other resources
context.close();
}
}
Error Quick Reference
| Symptom | Root Cause | Fix |
|---|---|---|
| Connection refused / connection timeout on startup | RabbitMQ not started, port/host unreachable, firewall blocked | telnet host 5672 / RabbitMQ logs / container port mapping |
| ACCESS_REFUSED - Login was refused on startup | Wrong username/password or no vhost permission | RabbitMQ management console Users / vhost permissions |
| NOT_FOUND - no exchange ‘direct.biz.ex’ on startup | Exchange not declared successfully or declaration order/permission issue | Ensure <rabbit:admin …/> exists and connection normal |
| Send no exception but queue has no message | Used convertAndSend(routingKey, msg) via default exchange, routingKey≠queue name causing routing failure | Use convertAndSend(exchange, routingKey, msg) |
| receiveAndConvert(“myqueue”) returns null | Queue empty/not yet delivered/taken by other consumer | Confirm routing hit |
| PRECONDITION_FAILED - inequivalent arg ‘durable’ on startup | Same name exchange/queue exists but with different attributes | Unify durable/auto-delete configuration |
| Spring XML parsing reports schema/DTD related error | Cannot load spring-rabbit.xsd | Ensure schema accessible |
| Direct exchange bound but still not routing | routingKey not exact match; binding key wrong | direct requires exact match |