What: This module integrates Amazon SQS with Spring applications, providing annotation-driven listeners (via @SqsListener and programmatic listener containers, backed by an asynchronous processing runtime.
Why: The SQS integration underwent a major redesign in Spring Cloud AWS 3.0 to address limitations in previous versions and build on AWS SDK v2’s async API, and it was announced as GA in 2023.
Who: This document is meant for maintainers, contributors, and readers who want to understand the module’s internal structure and design. It describes the module in two phases: an assembly phase at startup, and a runtime phase where messages are polled, processed, and acknowledged. It focuses on the high-level structure and provides shared terminology for discussing the module’s flows and components.
The module is organized into two phases with different responsibilities:
-
Assembly phase: At startup, Spring detects @SqsListener annotations, creates listener endpoints, and wires MessageListenerContainer instances through a factory and registry. This is similar to patterns used by Spring for Apache Kafka and other Spring messaging projects.
-
Runtime phase: When containers start, they run an asynchronous pipeline that polls SQS, invokes the listener, and acknowledges messages. This pipeline builds on AWS SDK v2’s async API (SqsAsyncClient) and uses a composable component model, including adaptive backpressure controls. While reusing familiar Spring abstractions such as MessageListener, the async processing pipeline is a module-specific design introduced in Spring Cloud AWS 3.0.
This separation keeps startup wiring concerns independent from message processing concerns, and makes the runtime pipeline easier to reason about and evolve without changing the assembly flow.
The assembly phase wires listener containers at startup. The flow is:
- SqsListenerAnnotationBeanPostProcessor detects @SqsListener annotations during bean post-processing
- For each annotation, it creates an Endpoint describing the listener
- Endpoints are registered with the EndpointRegistrar
- The registrar delegates to SqsMessageListenerContainerFactory to create containers
- Containers are registered in the MessageListenerContainerRegistry, which manages their lifecycle
flowchart LR
A["SqsListenerAnnotationBeanPostProcessor"] --> B["Endpoint\ncreated"]
B --> C["EndpointRegistrar"]
C --> D["SqsMessageListenerContainerFactory"]
D --> E["MessageListenerContainer"]
E --> F["MessageListenerContainerRegistry"]
F --> G["Container lifecycle start\n(transition to runtime)"]
When the MessageListenerContainerRegistry starts its containers, each container assembles its processing pipeline and begins polling for messages. The ContainerComponentFactory creates the runtime components and wires them together.
SQS is inherently I/O-bound. Every poll is a network call to AWS, and every acknowledgement is a batch-delete call. A blocking model would tie up threads waiting on these responses.
The pipeline is built on SqsAsyncClient, where receiveMessage() and deleteMessageBatch() both return CompletableFuture. This keeps polling and acknowledgement non-blocking and makes concurrency primarily a matter of configured in-flight capacity rather than thread availability.
The runtime is structured as a composable pipeline with four stages — ingress, dispatch, processing, and acknowledgement — each mapped to dedicated components within each listener container:
- Ingress — MessageSource: Polls SQS for messages and converts them to Spring Message objects. Uses a BackPressureHandler to gate polling based on in-flight message capacity
- Dispatch — MessageSink: Dispatches messages to the processing pipeline. Composable component with implementations such as FanOutMessageSink (single-message), BatchMessageSink, OrderedMessageSink, and MessageGroupingSinkAdapter (FIFO)
- Processing — MessageProcessingPipeline: Chains together the stages that process each message:
- MessageInterceptor - before/after processing hooks
- MessageListener - invokes the user's @SqsListener method
- ErrorHandler - handles processing failures
- AcknowledgementHandler - triggers acknowledgement (deletion from SQS)
- Acknowledgement — AcknowledgementProcessor: Acknowledges processed messages by deleting them from SQS
- AcknowledgementResultCallback: Notified after acknowledgement succeeds or fails
These components are assembled at container start and interact through interfaces, which makes it possible to swap or extend individual stages.
flowchart LR
A["MessageSource\n(polls SQS)"] --> B["MessageSink"]
A --> BP["BackPressureHandler"]
B --> P
subgraph P ["MessageProcessingPipeline"]
direction LR
P1["Interceptor\n(before)"] --> P2["Listener"]
P2 --> P3["ErrorHandler"]
P3 --> P4["Interceptor\n(after)"]
P4 --> P5["AcknowledgementHandler"]
end
P --> D["AcknowledgementProcessor\n(deletes from SQS)"]
D --> E["AcknowledgementResultCallback"]