Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 76

Producing RabbitMQ Messages in Spring Boot

Recommended resources

Hello! Welcome back to our series on asynchronous patterns.

In our last lesson, you successfully set up a local RabbitMQ server using Docker Compose. This provided us with the essential message broker infrastructure. You now have a running RabbitMQ instance and know how to connect a Spring Boot application to it using application.properties.

Today, we'll build on that foundation. Our goal is to implement a message producer in Spring Boot to send messages to a RabbitMQ exchange. You will create a Spring Boot application that acts as a publisher, sending a message that will land in your RabbitMQ instance. This is the first half of the publish/subscribe pattern and a core skill for building event-driven systems.

The Central Role of Exchanges

Before we write any Java code, it's crucial to understand a core concept in RabbitMQ that we've only touched on briefly: the exchange.

In RabbitMQ, producers don't send messages directly to queues. Instead, they publish messages to an exchange. The exchange then becomes responsible for routing the messages to different queues based on a set of rules. This is a powerful mechanism that decouples the producer from the consumer. The producer doesn't need to know which queues (or how many) will receive its message.

To solidify this concept, let's turn to the official RabbitMQ tutorials.

RabbitMQ tutorial - Publish/Subscribe

The document 'RabbitMQ tutorial - Publish/Subscribe' provides an excellent, concise explanation of what exchanges are and why they are fundamental to RabbitMQ's messaging model.

Please read the section titled 'Exchanges'. Focus on understanding the relationship between the producer, the exchange, and the queue. Also, note the different types of exchanges mentioned: direct, topic, headers, and fanout. We will be using a topic exchange in our example.

As you read, the key takeaway is this: the producer's only job is to send a message to a specific exchange with a routing key. The exchange type then determines how it uses that routing key to deliver the message to any bound queues.


1. Setting Up Your Spring Boot Producer Application

Let's start by creating a new Spring Boot project. We will configure it to connect to the RabbitMQ instance you set up in the previous lesson.

Getting Started | Messaging with RabbitMQ - Spring

The official Spring guide 'Messaging with RabbitMQ' shows exactly which dependencies are needed for our project. We will use Spring Initializr to bootstrap our application.

Follow the steps in the 'Starting with Spring Initializr' section. When you get to step 3, you only need to add one dependency: Spring for RabbitMQ (which has the ID 'amqp'). We will handle the Docker part manually, as we learned in the last lesson.

After generating and opening the project in your IDE, navigate to src/main/resources/application.properties and add the connection details for your RabbitMQ server. These should match the credentials you configured in your docker-compose.yml file from the last lesson.

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=myuser
spring.rabbitmq.password=mypassword

Make sure your RabbitMQ container is running by executing docker-compose up -d in the directory containing your docker-compose.yml file.


2. Declaring the Messaging Infrastructure

With our project set up, we need to tell Spring AMQP about the exchange and queue we want to use. We do this by declaring them as Spring beans. Spring will then ensure this infrastructure is created on the RabbitMQ broker when the application starts.

We'll use a Topic Exchange, which is one of the most flexible types. It routes messages based on pattern matching with the routing key.

Let's add these beans to your main application class.

Getting Started | Messaging with RabbitMQ - Spring

The Spring guide provides a clear example of how to declare a queue, a topic exchange, and the binding between them as Spring beans.

In the MessagingRabbitmqApplication.java code sample, find the methods queue(), exchange(), and binding(). Add these three bean-defining methods to your own main application class. We will not be adding the listener-related beans (container, listenerAdapter) in this lesson.

Your main application class should now look something like this:

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.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MessagingRabbitmqApplication {

    static final String topicExchangeName = "my-topic-exchange";
    static final String queueName = "my-queue";

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

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

    @Bean
    Binding binding(Queue queue, TopicExchange exchange) {
        // This binding will route messages with routing keys that start with "foo.bar."
        return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#");
    }

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

A quick breakdown of the binding:

  • bind(queue).to(exchange) connects our queue to our exchange.
  • .with("foo.bar.#") is the binding key. For a topic exchange, this key defines the pattern of routing keys this queue is interested in.
    • * can substitute for exactly one word.
    • # can substitute for zero or more words.
  • In our case, this queue will receive any message sent to the exchange with a routing key like foo.bar.baz, foo.bar.test, etc.

3. Implementing the Message Producer

Now for the main event: sending the message. The core component for this in Spring AMQP is the RabbitTemplate. Spring Boot automatically creates and configures a RabbitTemplate bean for you, so all we need to do is inject it and use it.

To keep things simple, we'll create a CommandLineRunner. This is a special type of Spring bean that executes its run method once, right after the application starts up. It's perfect for a demonstration.

Getting Started | Messaging with RabbitMQ - Spring

The Spring guide demonstrates a simple producer using a CommandLineRunner. This is a great way to test our setup without building a full REST API.

Create a new Java class named Runner.java in the same package as your main application. Copy the code from the guide, but note we will be making a small adjustment since we removed the Receiver part for this lesson.

Here is the simplified Runner class for our producer-only application. Create a new file Runner.java and add this code:

package com.example.messagingrabbitmq; // Use your own package name

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;

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

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Sending message...");
        String message = "Hello from our Spring Boot producer!";
        
        // The magic happens here!
        rabbitTemplate.convertAndSend(MessagingRabbitmqApplication.topicExchangeName, "foo.bar.baz", message);
        
        System.out.println("Message sent!");
    }
}

The most important line is rabbitTemplate.convertAndSend(...). Let's analyze its arguments:

  1. MessagingRabbitmqApplication.topicExchangeName: The name of the exchange to publish to.
  2. "foo.bar.baz": The routing key for this specific message. Because this key matches the foo.bar.# pattern in our binding, the exchange will route this message to my-queue.
  3. message: The actual message payload. Spring's convertAndSend will automatically convert this String into a format RabbitMQ can handle.
Test your understanding!

Imagine you are building an e-commerce platform. You have a topic exchange named events-exchange. You want to publish an event every time a new order is placed for a digital product.

What would be an appropriate routing key for this event, and how would you change the convertAndSend call to publish it?

Show answer

An appropriate routing key could be "order.placed.digital". This hierarchical structure is clear and allows for flexible routing.

The convertAndSend call would look like this:
rabbitTemplate.convertAndSend("events-exchange", "order.placed.digital", orderEventObject);

(Where orderEventObject would be an object containing details about the order).


4. Running the Application and Verifying the Result

You are now ready to send your first message!

  1. Run your Spring Boot application. You can do this from your IDE.
  2. Watch the console output. You should see the "Sending message..." and "Message sent!" logs from our Runner.
  3. Open the RabbitMQ Management UI in your browser at http://localhost:15672 (and log in with myuser/mypassword).

Now, let's verify everything worked as expected:

  • Click on the Exchanges tab. You should see our my-topic-exchange in the list.
  • Click on the Queues tab. You should see my-queue in the list. Notice the "Messages" column shows 1.
  • Click on the name my-queue to go to its detail page.
  • Scroll down to the Get messages panel. Click the "Get Message(s)" button.

You should see your message, "Hello from our Spring Boot producer!", in the payload.

(Image shows the RabbitMQ UI with the message payload visible after clicking "Get Message(s)").

Congratulations! You have successfully built a message producer that publishes a message to a topic exchange, which then routes it to a queue.


Conclusion

In this lesson, you took a significant step into practical event-driven architecture. You've learned how to harness Spring Boot and Spring AMQP to produce messages and send them to a RabbitMQ exchange.

Key Takeaways:

  • Producers publish messages to exchanges, not directly to queues. This is a fundamental concept for decoupling services.
  • In Spring AMQP, you declare your messaging infrastructure (queues, exchanges, bindings) as @Beans.
  • The RabbitTemplate is the primary Spring component for sending messages.
  • The convertAndSend(exchange, routingKey, message) method is your main tool for publishing messages. The routing key is critical for directing messages through the exchange.

What's Next?

Our message is now sitting in a queue, waiting to be processed. The next logical step is to build the other half of the system: a consumer. In the next lesson, you will implement a message consumer in Spring Boot to receive and process messages from the RabbitMQ queue. We will create a listener that automatically picks up the message you just sent.