RabbitMQ / Messaging

RabbitMQ integration for Spring Boot microservices

Spring Middleware provides a platform-aligned RabbitMQ integration for Spring Boot microservices. It standardizes producers, consumers, handlers, and lifecycle management while keeping acknowledgement, rollback, and processing flow explicit.

RabbitMQ producers RabbitMQ consumers Annotation-driven resources Handlers and listeners Acknowledge and rollback Lifecycle management

What the RabbitMQ module does

The RabbitMQ module provides an annotation-driven framework for creating producers and consumers, wiring processing hooks, managing broker resources, and controlling consumer behavior around acknowledgement, transactions, and failures.

  • Discover producers and consumers from scanned packages
  • Create runtime resources through JmsFactory
  • Attach handlers, listeners, and error handlers by annotation rules
  • Support queue, topic, binding, converter, and lifecycle concerns

Why this model matters

RabbitMQ integrations often drift into duplicated setup spread across services. Spring Middleware turns that into a platform concern, while still keeping the broker model, transaction behavior, and message processing path explicit.

  • Explicit lifecycle: producers and consumers are real runtime resources.
  • Explicit hooks: handlers, listeners, and error handlers are part of the model.
  • Explicit processing semantics: acknowledge, commit, and rollback stay visible.

High-level RabbitMQ flow

Producer and consumer resources are wired by the module, while processing hooks are attached through annotation-based discovery and matching rules.

Producer Resource RabbitMQ Broker Consumer Resource Processing hooks listeners • handlers • error handlers acknowledge • commit • rollback

Producer and consumer resources

The module discovers producer and consumer resource classes and creates runtime objects from them. This keeps RabbitMQ wiring centralized and annotation-driven.

Producer example

@JmsProducer(bindings = {
    @JmsBinding(routingKey = "news.uk"),
    @JmsBinding(routingKey = "news.es")
})
@JmsDestination(
    name = "amq.topic",
    exchange = "amq.topic",
    destinationType = DestinationType.TOPIC
)
public class JmsProducerNewsWorld extends JmsProducerResource<TestingMessage> {

    public JmsProducerNewsWorld(
            String routingKey,
            ObjectPool<JmsConnection> connectionPool,
            JmsSessionParameters jmsSessionParameters,
            JmsResourceDestination jmsResourceDestination,
            Class<TestingMessage> clazz
    ) {
        super(routingKey, connectionPool, jmsSessionParameters, jmsResourceDestination, clazz);
    }
}

Consumer example

@JmsConsumer
@JmsDestination(name = "information-uk", destinationType = DestinationType.QUEUE)
public class JmsConsumerUK extends JmsConsumerResource<TestingMessage> {

    @Override
    public void process(TestingMessage testingMessage, Properties properties) {
        logger.info("Message Received " + testingMessage.getMessage() + "-UK");
    }
}

Handlers, listeners, and error handlers

The RabbitMQ module provides three complementary extension points for processing control, cross-cutting behavior, and failure handling.

@JmsListener

Application-level listeners executed before processing. They are useful for tracing, metrics, audit, conditional short-circuiting, and other cross-cutting concerns.

@JmsHandler

A single wrapper component around the processing flow. It is the right place for transactional setup, conversion, MDC, resource opening, and finally-style cleanup behavior.

@JmsErrorHandler

Error handler components invoked when processing fails. They are useful for alerts, persistence of failures, retry routing, and dead-letter patterns.

Handler example

@JmsHandler(ConsumerHandler.class)
public class ConsumerHandlerResource<T> extends JmsHandlerResource<String, T> {

    @Override
    public String handleBeforeProcessingMessage(T message, Properties properties) {
        return "dbSession";
    }

    @Override
    public void handleFinallyConsumingMessage(String handlingMessage) {
        // cleanup
    }
}

Listener example

@JmsListener
public class JmsListenerAll implements JmsResourceListener {

    @Override
    public void onBeforeProcessingMessage(Properties properties) {
        logger.info("[ALL] Listener on before process message");
    }
}

Annotation matching rules

The module uses marker-based matching so processing components can be attached selectively to producers or consumers.

01

Discover component classes

The module scans for classes annotated with @JmsHandler, @JmsListener, and @JmsErrorHandler.

02

Resolve marker annotation

The value() of the framework annotation points to the user-defined marker annotation used for matching.

03

Attach component to resource

The resource gets the matching handler, listener, or error handler according to the module's matching rules.

Common matching modes

  • JmsAll — apply to all resources
  • JmsAllConsumers — apply only to consumers
  • JmsAllProducers — apply only to producers
  • User marker annotations — apply to explicitly marked resource classes

Important multiplicity rules

  • Only one @JmsHandler may be attached to a given resource
  • Multiple listeners and error handlers may apply to the same resource
  • Listener and error-handler ordering is controlled by priority()
  • Higher numeric priority runs earlier

Transactions and acknowledgement modes

Consumer runtime behavior depends on two explicit configuration knobs: transaction mode and acknowledgement mode.

Transacted sessions

When transacted = true, the consumer uses a transacted session and explicitly commits on success or rolls back on failure.

  • Successful processing triggers session.commit()
  • Processing failure triggers session.rollback()
  • Use deliberately, because transaction semantics affect latency and complexity

CLIENT_ACKNOWLEDGE

When the session is not transacted and the acknowledge mode is CLIENT_ACKNOWLEDGE, the message must be acknowledged explicitly.

  • By default, the module may attach DefaultJmsAcknowledgeListener
  • If messages are not acknowledged, they may be redelivered
  • Acknowledge behavior is part of the processing model, not an invisible side effect

Runtime processing sequence

Consumer message processing follows a well-defined sequence so application behavior remains understandable.

01

Receive and convert message

The consumer receives the message, reads its properties, and converts the body to the target type.

02

Run listeners and handler

Registered listeners are executed in priority order and the configured handler may wrap the processing flow.

03

Commit, acknowledge, or handle failure

On success the module commits or acknowledges as needed; on failure it rolls back and delegates to error handlers.

Best practices

The RabbitMQ module is most effective when processing hooks stay small, lifecycle cleanup is explicit, and failure handling is centralized.

Keep handlers focused

A handler should usually implement one kind of wrapper logic, such as transaction or context setup, instead of mixing multiple concerns into one large component.

Always clean up in finally hooks

If a handler opens DB sessions, MDC, or external resources, release them in the finally phase so consumers do not leak runtime state.

Centralize failure routing

Prefer error handlers for alerts, metrics, persistence of failures, or dead-letter routing instead of scattering that logic inside each consumer.

Marker-based error handling example

Marker annotations let applications attach platform hooks only where they are needed.

Error handler example

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DltError {}

@JmsErrorHandler(value = DltError.class)
public class DltErrorHandler<T> implements JmsResourceErrorHandler<T> {

    @Override
    public void handleError(ErrorHandlerContext<T> ctx) {
        dltProducer.send(ctx.getMessage(), ctx.getProperties());
    }
}

Usage on a consumer

@DltError
@JmsConsumer
@JmsDestination(name = "orders", destinationType = DestinationType.QUEUE)
public class OrdersConsumer extends JmsConsumerResource<Order> {

    @Override
    public void process(Order order, Properties properties) {
        // ...
    }
}