Spring Boot + RabbitMQ / Simple minimal example
It shows:
- one REST point
- one producer that sends a message to RabbitMQ
- one consumer that receives it
- Docker Compose to start RabbitMQ and the app
The idea:
- You call HTTP endpoint: POST /api/messages
- Spring Boot sends the message to RabbitMQ
- RabbitMQ stores/routes it
- Spring Boot listener consumes it and prints it
1) Components
Spring Boot application is the Java application itself.
MessageController - receives HTTP requests
MessageProducer - sends messages to RabbitMQ
MessageListener - receives messages from RabbitMQ
RabbitConfig - defines queue/exchange/binding
RabbitMessage - simple payload object
RabbitMQ is the message broker.
Queue - where messages wait
Exchange - receives messages from producers and routes them
Routing key - helps exchange decide where to send messages
Binding - connection rule between exchange and queue
Docker Compose starts both RabbitMQ container and Spring Boot app container.
This way we can run the whole demo with one command.
2) Project structure
spring-rabbit-demo/
├─ src/
│ ├─ main/
│ │ ├─ java/com/example/demo/
│ │ │ ├─ DemoApplication.java
│ │ │ ├─ config/RabbitConfig.java
│ │ │ ├─ model/RabbitMessage.java
│ │ │ ├─ messaging/MessageProducer.java
│ │ │ ├─ messaging/MessageListener.java
│ │ │ └─ controller/MessageController.java
│ │ └─ resources/
│ │ └─ application.properties
├─ Dockerfile
├─ docker-compose.yml
└─ pom.xml
3) Pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
Main dependencies:
- spring-boot-starter-web / Used to expose HTTP endpoints (REST API)
- spring-boot-starter-amqp / Used for RabbitMQ integration (AMQP = Advanced Message Queuing Protocol)
- spring-boot-starter / Core Spring Boot basics
- spring-boot-maven-plugin / Allows us to package the app as an executable JAR
4) Main application class
DemoApplication.java
This class boots the entire application context.
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
5) RabbitMQ configuration
package com.example.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
public static final String QUEUE_NAME = "demo.queue";
public static final String EXCHANGE_NAME = "demo.exchange";
public static final String ROUTING_KEY = "demo.routing.key";
@Bean
public Queue demoQueue() {
return new Queue(QUEUE_NAME, true);
}
@Bean
public DirectExchange demoExchange() {
return new DirectExchange(EXCHANGE_NAME);
}
@Bean
public Binding demoBinding(Queue demoQueue, DirectExchange demoExchange) {
return BindingBuilder
.bind(demoQueue)
.to(demoExchange)
.with(ROUTING_KEY);
}
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
6) Message payload object
package com.example.model;
public class RabbitMessage {
private String text;
public RabbitMessage() {}
public RabbitMessage(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
7) Producer
package com.example.messaging;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import com.example.config.RabbitConfig;
import com.example.model.RabbitMessage;
@Service
public class MessageProducer {
private final RabbitTemplate rabbitTemplate;
public MessageProducer(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void sendMessage(RabbitMessage message) {
rabbitTemplate.convertAndSend(
RabbitConfig.EXCHANGE_NAME,
RabbitConfig.ROUTING_KEY,
message
);
System.out.println(">>> PRODUCER sent message: " + message.getText());
}
}
8) Consumer / Listener
package com.example.messaging;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import com.example.config.RabbitConfig;
import com.example.model.RabbitMessage;
@Component
public class MessageListener {
@RabbitListener(queues = RabbitConfig.QUEUE_NAME)
public void receiveMessage(RabbitMessage message)
throws InterruptedException {
Thread.sleep(5000);
System.out.println("<<< CONSUMER received message: " + message.getText());
}
}
9) REST controller
package com.example.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;
import com.example.messaging.MessageProducer;
import com.example.model.RabbitMessage;
@RestController
@RequestMapping("/api/messages")
public class MessageController {
private final MessageProducer;
public MessageController(MessageProducer messageProducer) {
this.messageProducer = messageProducer;
}
@PostMapping
public ResponseEntity<String> sendMessage(@ResponseBody RabbitMessage message) {
messageProducer.sendMessage(message);
return ResponseEntity.ok("Message sent to RabbitMQ: " + message.getText());
}
}
10) Application properties
resource/application.properties
Application name (optional, only cosmetic/logging).
In Docker Compose, the RabbitMQ container name will be "rabbitmq".
yml
spring.application.name=spring-rabbit-demo
spring.rabbitmq.host=rabbitmq
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
server.port=9090
11) Dockerfile
yml
FROM maven:3.9.8-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY --from=build /app/target
12) Docker Compose
yml
services:
rabbitmq:
image: rabbitmq:3-management
container_name: rabbitmq
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
app:
build: .
container_name: spring-rabbit-demo
ports:
- "9090:9090"
depends_on:
- rabbitmq
13) Run everything
Change port to 9090 if needed.
docker ps
docker container prune -f
docker compose down
docker compose build --no-cache
docker compose up
When everything starts:
- Spring Boot app: http://localhost:9090
- RabbitMQ UI: http://localhost:15672
- RabbitMQ login:
- username: guest
- password: guest
14) How to test
curl -X POST http://localhost:9090/api/messages \
-H "Content-Type: application/json" \
-d "{\"text\":\"Hello RabbitMQ from Spring Boot\"}"
Expected HTTP response:
Message sent to RabbitMQ: Hello RabbitMQ from Spring Boot
And in app logs you should see:
>>> PRODUCER sent message: Hello RabbitMQ from Spring Boot
<<< CONSUMER received message: Hello RabbitMQ from Spring Boot
RabbitMQ’s UI is not beginner-friendly at all the first time.
Look at Queus and Streams and click demo.queue
Important counters:
- Ready:
- Messages waiting in queue
- NOT yet consumed
- Unacked
- Messages being processed by consumer
- Not yet confirmed
In this app (without the slow down trick), these will often be 0
because the messages are consumed instantly.