The new Certification Class of Learn Spring Security is out:

>> CHECK OUT THE COURSE

1. Overview

In this article, we will explore Messaging-based communication over AMQP protocol using Spring AMQP framework. First, we’ll cover some of the key concepts of messaging, and we’ll move on to practical examples in section 5.

1.1. Maven Dependencies

To use spring-amqp and spring-rabbit in your project, just add these dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-amqp</artifactId>
        <version>1.6.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>1.6.6.RELEASE</version>
    </dependency>
</dependencies>

You will find the newest versions in the Maven repository.

2. Messaging Based Communication

Messaging is a technique for inter-application communication that relies on asynchronous message-passing instead of synchronous request response-based architecture. Producer and consumer of messages are decoupled by an intermediate messaging layer known as the message broker. Message broker provides features like persistent storage of messages, message filtering, message transformation, etc.

In a case of intercommunication between applications written in Java, JMS (Java Message Service) API is commonly used for sending and receiving messages. For interoperability between different vendors and platforms,  we won’t be able to use JMS clients and brokers. This is where AMQP comes in handy.

3. AMQP – Advanced Message Queuing Protocol

AMQP is an open standard wire specification for asynchronous messaging based communication. It provides a description on how a message should be constructed. Every byte of transmitted data is specified.

3.1. How AMQP is Different From JMS

Since AMQP is a platform neutral binary protocol standard, this characteristic allows libraries to be written in different programming languages, and to run on different operating systems and CPU architectures.

There is no vendor based protocol lock in, as in the case of migrating from one JMS broker to another. For more details refer to JMS vs AMQP and Understanding AMQP. Some of the widely used AMQP brokers are RabbitMQ, OpenAMQ, and StormMQ.

3.2. AMQP Entities

AMQP entities comprise of Exchanges, Queues, and Bindings:

  • Exchanges are like post offices or mailboxes and clients always publish a message to an AMQP exchange
  • Queues bind to exchange using the binding key. A binding is “link” that you set up to bind a queue to an exchange
  • Messages are sent to message broker/exchange with a routing key. The exchange then distributes copies of messages to queues. Exchange provides the abstraction to achieve different messaging routing topologies like fanout, hierarchical routing, etc.

3.3. Exchange Types

Exchanges are AMQP entities where messages are sent. Exchanges take a message and route it into zero or more queues. There are four built in exchange types:

  • Direct Exchange
  • Fanout Exchange
  • Topic Exchange
  • Headers Exchange

For more details, have a look at AMQP Concepts and Routing Topologies.

4. Spring AMQP

Spring AMQP comprise of two modules: spring-amqp and spring-rabbit, each represented by a jar in the distribution. spring-

Spring-amqp is the base abstraction and you will find classes that represent the core AMQP model: Exchange, Queue and Binding. 

We will be covering spring-rabbit specific features below in the article.

4.1. spring-amqp Features

  • Template-based abstraction – AMQPTemplate interface defines all basic operations for sending/receiving messages, with RabbitTemplate as the implementation
  • Publisher Confirmations/Consumer Acknowledgements support
  • AmqpAdmin tool that allows performing basic operations

4.2. spring-rabbit Features

AMQP model and related entities we discussed above are generic and applicable to all implementations. But there are some features which are specific to the implementation. A couple of those spring-rabbit features is explained below.

Connection Management Support – org.springframework.amqp.rabbit.connection.ConnectionFactory interface is the central component for managing the connections to RabbitMQ broker. The responsibility of CachingConnectionFactory implementation of ConnectionFactory interface is to provide an instance of org.springframework.amqp.rabbit.connection.Connection interface. Please note that spring-rabbit provides Connection interface as a wrapper over RabbitMQ client com.rabbitmq.client.Connection interface.

Asynchronous Message Consumption For asynchronous message consumption, two key concepts are a callback and a container. The callback is where your application code will be integrated with the messaging system.

One of the ways to code a callback is to provide an implementation of the MessageListener interface:

public interface MessageListener {
    void onMessage(Message message);
}

In order to have a clear separation between application code and messaging API, Spring AMQP provides MessageListenerAdapter also. Here is a Spring bean configuration example for listener container:

MessageListenerAdapter listener = new MessageListenerAdapter(somePojo);
listener.setDefaultListenerMethod("myMethod");

Now that we saw the various options for the Message-listening callback, we can turn our attention to the container. Basically, the container handles the “active” responsibilities so that the listener callback can remain passive. The container is an example of a lifecycle component. It provides methods for starting and stopping.

When configuring the container, you are essentially bridging the gap between an AMQP Queue and the MessageListener instance. By default, the listener container will start a single consumer which will receive messages from the queues.

5. Send and Receive Messages Using Spring AMQP

Here are the steps to send and receive a message via Spring AMQP:

  1. Setup and Start RabbitMQ broker – RabbitMQ installation and setup is straightforward, just follow the steps mentioned here
  2. Setup Java Project Create a Maven based Java project with dependencies mentioned above
  3. Implement Message Producer – We can use RabbitTemplate to send a “Hello, world!” message:
    AbstractApplicationContext ctx
      = new ClassPathXmlApplicationContext("beans.xml");
    AmqpTemplate template = ctx.getBean(RabbitTemplate.class);
    template.convertAndSend("Hello, world!");
    
  4. Implement Message Consumer – As discussed earlier, we can implement message consumer as a POJO:
    public class Consumer {
        public void listen(String foo) {
            System.out.println(foo);
        }
    }
  5. Wiring Dependencies  We will be using following spring bean configuration for setting up queues, exchange, and other entities. Most of the entries are self-explanatory. Queue named “myQueue” is bound to Topic Exchange “myExchange” using “foo.*” as the binding key. RabbitTemplate has been set up to send messages to “myExchange” exchange with “foo.bar” as the default routing key. ListenerContainer ensures an asynchronous delivery of messages from “myQueue” queue to listen() method of Foo class:
    <rabbit:connection-factory id="connectionFactory"
      host="localhost" username="guest" password="guest" />
    
    <rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
        exchange="myExchange" routing-key="foo.bar" />
    
    <rabbit:admin connection-factory="connectionFactory" />
    
    <rabbit:queue name="myQueue" />
    
    <rabbit:topic-exchange name="myExchange">
        <rabbit:bindings>
            <rabbit:binding queue="myQueue" pattern="foo.*" />
        </rabbit:bindings>
    </rabbit:topic-exchange>
    
    <rabbit:listener-container connection-factory="connectionFactory">
        <rabbit:listener ref="consumer" method="listen" queue-names="myQueue" />
    </rabbit:listener-container>
    
    <bean id="consumer" class="com.baeldung.springamqp.consumer.Consumer" />

    Note: By default, in order to connect to local RabbitMQ instance, use username “guest” and password “guest”.

  6. Run the Application: 
  • The first step is to make sure RabbitMQ is running, default port being 5672
  • Run the application by running Producer.java, executing the main() method
  • Producer sends the message to the “myExchange” with “foo.bar” as the routing key
  • As per the binding key of “myQueue”, it receives the message
  • Foo class which is a consumer of “myQueue” messages with listen() method as the callback receives the message and prints it on the console

6. Conclusion

In this article, we have covered messaging based architecture over AMQP protocol using Spring AMQP for communication between applications.

The complete source code and all code snippets for this article are available on the GitHub project.

Go deeper into Spring Security with the course:

>> LEARN SPRING SECURITY