Skip to content

9.2.2 Sending messages with RabbitTemplate

At the core of Spring’s support for RabbitMQ messaging is RabbitTemplate. RabbitTemplate is similar to JmsTemplate and offers a similar set of methods. As you’ll see, however, some subtle differences align with the unique way that RabbitMQ works.

With regard to sending messages with RabbitTemplate, the send() and convertAndSend() methods parallel the same-named methods from JmsTemplate. But unlike the JmsTemplate methods, which route messages only to a given queue or topic, RabbitTemplate methods send messages in terms of exchanges and routing keys. Here are a few of the most relevant methods for sending messages with RabbitTemplate:

java
// Send raw messages
void send(Message message) throws AmqpException;
void send(String routingKey, Message message) throws AmqpException;
void send(String exchange, String routingKey, Message message) 
                          throws AmqpException;

// Send messages converted from objects
void convertAndSend(Object message) throws AmqpException;
void convertAndSend(String routingKey, Object message) 
                          throws AmqpException;
void convertAndSend(String exchange, String routingKey, Object message) throws AmqpException;

// Send messages converted from objects with post-processing
void convertAndSend(Object message, MessagePostProcessor mPP) 
                          throws AmqpException;
void convertAndSend(String routingKey, Object message, 
                          MessagePostProcessor messagePostProcessor) 
                          throws AmqpException;
void convertAndSend(String exchange, String routingKey, Object message, 
                          MessagePostProcessor messagePostProcessor) 
                          throws AmqpException;

As you can see, these methods follow a pattern similar to their twins in JmsTemplate. The first three send() methods all send a raw Message object. The next three convertAndSend() methods accept an object that will be converted to a Message behind the scenes before being sent. The final three convertAndSend() methods are like the previous three, but they accept a MessagePostProcessor that can be used to manipulate the Message object before it’s sent to the broker.

These methods differ from their JmsTemplate counterparts in that they accept String values to specify an exchange and routing key, rather than a destination name (or Destination object). The methods that don’t take an exchange will have their messages sent to the default exchange. Likewise, the methods that don’t take a routing key will have their messages routed with a default routing key.

Let’s put RabbitTemplate to work sending taco orders. One way you can do that is by using the send() method, as shown in listing 9.5. But before you can call send(), you’ll need to convert a TacoOrder object to a Message. That could be a tedious job, if not for the fact that RabbitTemplate makes its message converter readily available with a getMessageConverter() method.

Listing 9.5 Sending a message with RabbitTemplate.send()

java
package tacos.messaging;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import
    org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tacos.Order;

@Service
public class RabbitOrderMessagingService
          implements OrderMessagingService {
  private RabbitTemplate rabbit;

  @Autowired
  public RabbitOrderMessagingService(RabbitTemplate rabbit) {
    this.rabbit = rabbit;
  }

  public void sendOrder(TacoOrder order) {
    MessageConverter converter = rabbit.getMessageConverter();
    MessageProperties props = new MessageProperties();
    Message message = converter.toMessage(order, props);
    rabbit.send("tacocloud.order", message);
  }
}

Once you have a MessageConverter in hand, it’s simple work to convert a TacoOrder to a Message. You must supply any message properties with a MessageProperties, but if you don’t need to set any such properties, a default instance of MessageProperties is fine. Then, all that’s left is to call send(), passing in the exchange and routing key (both of which are optional) along with the message. In this example, you’re specifying only the routing key—tacocloud.order—along with the message, so the default exchange will be used.

Speaking of default exchanges, the default exchange name is "" (an empty String), which corresponds to the default exchange that’s automatically created by the RabbitMQ broker. Likewise, the default routing key is "" (whose routing is dependent upon the exchange and bindings in question). You can override these defaults by setting the spring.rabbitmq.template.exchange and spring.rabbitmq.template.routing-key properties as follows:

yaml
spring:
  rabbitmq:
    template:
      exchange: tacocloud.orders
      routing-key: kitchens.central

In this case, all messages sent without specifying an exchange will automatically be sent to the exchange whose name is tacocloud.order. If the routing key is also unspecified in the call to send() or convertAndSend(), the messages will have a routing key of kitchens.central.

Creating a Message object from the message converter is easy enough, but it’s even easier to use convertAndSend() to let RabbitTemplate handle all of the conversion work for you, as shown next:

java
public void sendOrder(Order order) {
    rabbit.convertAndSend("tacocloud.order", order);
}

CONFIGURING A MESSAGE CONVERTER

By default, message conversion is performed with SimpleMessageConverter, which is able to convert simple types (like String) and Serializable objects to Message objects. But Spring offers several message converters for RabbitTemplate, including the following:

  • Jackson2JsonMessageConverter —— Converts objects to and from JSON using the Jackson 2 JSON processor
  • MarshallingMessageConverter —— Converts using a Spring Marshaller and Unmarshaller
  • SerializerMessageConverter —— Converts String and native objects of any kind using Spring’s Serializer and Deserializer abstractions
  • SimpleMessageConverter —— Converts String, byte arrays, and Serializable types
  • ContentTypeDelegatingMessageConverter —— Delegates to another MessageConverter based on the contentType header
  • MessagingMessageConverter —— Delegates to an underlying MessageConverter for the message conversion and to an AmqpHeaderConverter for the headers

If you need to change the message converter, just configure a bean of type MessageConverter. For example, for JSON-based message conversion, you can configure a Jackson2JsonMessageConverter like this:

java
@Bean
public MessageConverter messageConverter() {
    return new Jackson2JsonMessageConverter();
}

Spring Boot autoconfiguration will discover this bean and inject it into RabbitTemplate in place of the default message converter.

SETTING MESSAGE PROPERTIES

As with JMS, you may need to set some headers in the messages you send. For example, let’s say you need to send an X_ORDER_SOURCE for all orders submitted through the Taco Cloud website. When creating your own Message objects, you can set the header through the MessageProperties instance you give to the message converter. Revisiting the sendOrder() method from listing 9.5, you only need one additional line of code to set the header, as shown next:

java
public void sendOrder(TacoOrder order) {
  MessageConverter converter = rabbit.getMessageConverter();
  MessageProperties props = new MessageProperties();
  props.setHeader("X_ORDER_SOURCE", "WEB");
  Message message = converter.toMessage(order, props);
  rabbit.send("tacocloud.order", message);
}

When using convertAndSend(), however, you don’t have quick access to the MessageProperties object. A MessagePostProcessor can help you with that, though, as shown here:

java
public void sendOrder(TacoOrder order) {
  rabbit.convertAndSend("tacocloud.order.queue", order,
    new MessagePostProcessor() {
      @Override
      public Message postProcessMessage(Message message)
                  throws AmqpException {
        MessageProperties props = message.getMessageProperties();
        props.setHeader("X_ORDER_SOURCE", "WEB");
        return message;
      }
  });
}

Here you supply convertAndSend() with an anonymous inner-class implementation of MessagePostProcessor. In the postProcessMessage() method, you pull the MessageProperties from the Message and then call setHeader() to set the X_ORDER_SOURCE header.

Now that you’ve seen how to send messages with RabbitTemplate, let’s switch our focus over to the code that receives messages from a RabbitMQ queue.

Released under the MIT License.