Deal with RabbitMQ queues with Spring Boot

Rationale TL;DR

In a microservices world, avoiding coupling is always a good practice. Using a message broker might help avoiding coupling between microservices because:

  • One service does not need to know who is going to handle a task
  • That service shall not need to wait until the task is done

If that is feasible, then using a message broker can be of a great help.

Let’s assume we know the basic AMQP concepts, like the queue, exchange and binding concepts.

Needed dependencies

To pull in Spring Boot AMQP, we simply need to add the spring-boot-starter-amqp dependency. If we are using maven:

    <dependencies>
    ...
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
    ...
    </dependencies>

RabbitMQ and Spring Boot. Configuring a publisher.

With Spring Boot, we can easily create a publisher. To do so, we need to declare some beans. If we want to use a direct exchange, we need four beans:

  • A Queue bean
  • A DirectExchange bean
  • A Binding bean
  • And a RabbitTemplate bean

Bear in mind that we don’t need to give them specific names if we’re only using one bean of each type. Otherwise, we need to give them proper different names so that we can properly use them:

@Configuration
public class RabbitProducerMailConfig {
	@Value("${rabbit.mail.queue.name}")
	private String queueName;

	@Value("${rabbit.mail.exchange}")
	private String exchange;

	@Value("${rabbit.mail.routingkey}")
	private String routingKey;

	@Bean
	Queue queueMail() {
		return new Queue(queueName, true);
	}

	@Bean
	DirectExchange exchangeMail() {
		return new DirectExchange(exchange);
	}

	@Bean
	Binding bindingMail(Queue queueMail, DirectExchange exchangeMail) {
		return BindingBuilder.bind(queueMail).to(exchangeMail).with(routingKey);
	}

	@Bean
	public RabbitTemplate rabbitProducerTemplateMail(final ConnectionFactory connectionFactory) {
		final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
		rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
		return rabbitTemplate;
	}

	@Bean
	public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
		return new Jackson2JsonMessageConverter();
	}
}

Here, I’m declaring a queue bean called queueMail, a direct exchange called exchangeMail and then a binding called bindingMail. When injecting the bindingMail bean, I need to use the queue and exchange beans by name, note that I’m using the names I previosly gave them. This is because I have other beans of the same type, if I don’t use names Spring Boot will get confused.

I usually declare the needed beans on a specific class and then I declare another class that would be injected and used to publish messages.

The important stuff is the RabbitTemplate bean, that bean will be used to publish messages on the exchange.

RabbitMQ and Spring Boot. Registering a publisher.

Now, let’s create another class that will be used to publish messages on the exchange. That class will be injectable, so we need to use the @Service annotation. We need also to autowire the RabbitTemplate bean we declared previouly:

@Service
public class MailQueueProduder {

	private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MailQueueProduder.class);

	@Autowired
	@Qualifier("rabbitProducerTemplateMail")
	private RabbitTemplate rabbitTemplate;

	@Value("${rabbit.mail.routingkey}")
	private String routingKey;

	@Value("${rabbit.mail.exchange}")
	private String exchange;

	public void publishMessageInQueue(MailForQueue mailForQueue) {
		log.info("[MailQueueProduder.publishMessageInQueue] Publishing mail to {}", rabbitTemplate.getExchange());
		this.rabbitTemplate.convertAndSend(exchange, routingKey, mailForQueue);
	}

That was easy. We just needed to autowire the RabbitTemplate bean using the @Qualifier annotation. We could just use the right bean name, however I wanted to just see if that would also work, using the right name on the same class is easy, but in a different class makes things harder to read, that’s why I think the @Qualifier here makes sense, it makes it easier for me to understand that there’s a bean declared elsewhere.

RabbitMQ and Spring Boot. Using the publisher.

Now it’s just a matter of autowiring the MailQueueProduder and call its publishMessageInQueue method. Waayy!! The convertAndSend will serialize the MailForQueue instance into json using the Jackson2JsonMessageConverter bean and publish it in the DirectExchange exchange.