eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – Spring Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 30% off until 31st March, 2026

>> EXPLORE ACCESS NOW

Partner – Diagrid – NPI EA (cat= Testing)
announcement - icon

In distributed systems, managing multi-step processes (e.g., validating a driver, calculating fares, notifying users) can be difficult. We need to manage state, scattered retry logic, and maintain context when services fail.

Dapr Workflows solves this via Durable Execution which includes automatic state persistence, replaying workflows after failures and built-in resilience through retries, timeouts and error handling.

In this tutorial, we'll see how to orchestrate a multi-step flow for a ride-hailing application by integrating Dapr Workflows and Spring Boot:

>> Dapr Workflows With PubSub

Course – Spring Sale 2026 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 30% off until 31st March, 2026

>> EXPLORE ACCESS NOW

1. Overview

FIFO (First-In-First-Out) queues in AWS SQS are designed to ensure that messages are processed in the exact order they are sent and that each message is delivered only once.

Spring Cloud AWS v3 supports this functionality with easy-to-use abstractions that allow developers to handle FIFO queue features like message ordering and deduplication with minimal boilerplate code.

In this tutorial, we’ll explore three practical use cases of FIFO queues in the context of a financial transaction processing system:

  • Ensuring strict message ordering for transactions within the same account
  • Processing transactions from different accounts in parallel while maintaining FIFO semantics for each account
  • Handling message retries in case of processing failures, ensuring that retries respect the original message order

We’ll demonstrate these scenarios by setting up an event-driven application and creating live tests to assert the behavior is as expected, leveraging the environment and test setup from the Spring Cloud AWS SQS V3 introductory article.

2. Dependencies

To begin, we’ll manage dependencies and ensure version compatibility by importing the Spring Cloud AWS Bill of Materials (BOM):

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.awspring.cloud</groupId>
            <artifactId>spring-cloud-aws</artifactId>
            <version>3.2.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Next, we add the necessary Spring Cloud AWS starters for core functionality and SQS integration:

<dependency>
    <groupId>io.awspring.cloud</groupId>
    <artifactId>spring-cloud-aws-starter</artifactId>
</dependency>
<dependency>
    <groupId>io.awspring.cloud</groupId>
    <artifactId>spring-cloud-aws-starter-sqs</artifactId>
</dependency>

We’ll also include the Spring Boot Web Starter. Since we’re using the Spring Cloud AWS BOM, we don’t need to specify its version:

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

Finally, for testing, we’ll add dependencies for LocalStack and TestContainers with JUnit 5, Awaitility for asynchronous operation verification, and the Spring Boot Test Starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-testcontainers</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>localstack</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.awaitility</groupId>
    <artifactId>awaitility</artifactId>
    <scope>test</scope>
</dependency>

3. Setting Up the Local Test Environment

Next, we’ll configure our local test environment using Testcontainers and LocalStack. We’ll create a SqsLiveTestConfiguration class:

@Configuration
public class SqsLiveTestConfiguration {
    private static final String LOCAL_STACK_VERSION = "localstack/localstack:3.4.0";

    @Bean
    @ServiceConnection
    LocalStackContainer localStackContainer() {
        return new LocalStackContainer(DockerImageName.parse(LOCAL_STACK_VERSION));
    }
}

In this class, we declare our LocalStack test container as a Spring Bean and use the @ServiceConnection annotation to handle the wiring on our behalf.

4. Setting Up the Queue Names

We’ll define our SQS queue names within the application.yml file, leveraging Spring Boot’s configuration externalization features:

events:
  queues:
    fifo:
      transactions-queue: "transactions-queue.fifo"
      slow-queue: "slow-queue.fifo"
      failure-queue: "failure-queue.fifo"

This structure organizes our queue names under a hierarchical structure, making it easy to manage and access them in our application code. The .fifo suffix is mandatory for FIFO queues in SQS.

5. Setting Up the Application

Let’s illustrate these concepts with a practical example using a Transaction microservice. This service will process TransactionEvent messages, representing financial transactions that must be kept in order within each account.

First, we define our Transaction entity:

public record Transaction(UUID transactionId, UUID accountId, double amount, TransactionType type) {}

Along with a TransactionType enum:

public enum TransactionType {
    DEPOSIT,
    WITHDRAW
}

Next, we create the TransactionEvent:

public record TransactionEvent(UUID transactionId, UUID accountId, double amount, TransactionType type) {
    public Transaction toEntity() {
        return new Transaction(transactionId, accountId, amount, type);
    }
}

The TransactionService class handles the processing logic and maintains a simulated repository for testing purposes:

@Service
public class TransactionService {
    private static final Logger logger = LoggerFactory.getLogger(TransactionService.class);

    private final ConcurrentHashMap<UUID, List<Transaction>> processedTransactions = 
      new ConcurrentHashMap<>();

    public void processTransaction(Transaction transaction) {
        logger.info("Processing transaction: {} for account {}",
          transaction.transactionId(), transaction.accountId());
        processedTransactions.computeIfAbsent(transaction.accountId(), k -> new ArrayList<>())
          .add(transaction);
    }

    public List<Transaction> getProcessedTransactionsByAccount(UUID accountId) {
        return processedTransactions.getOrDefault(accountId, new ArrayList<>());
    }
}

6. Processing Events in Order

In our first scenario, we’ll create a listener that processes events and will create a test to assert that we receive the events in the same order as they were sent. We’ll use the @RepeatedTest annotation to run the test 100 times to make sure it’s consistent, and see how it behaves with a Standard SQS queue instead of a FIFO.

6.1. Creating the Listener

Let’s create our first listener to receive and process events in order.  We’ll use the @SqsListener annotation, leveraging Spring’s placeholder resolution to resolve the queue name from the application.yml file:

@Component
public class TransactionListener {
    private final TransactionService transactionService;

    public TransactionListener(TransactionService transactionService) {
        this.transactionService = transactionService;
    }

    @SqsListener("${events.queues.fifo.transactions-queue}")
    public void processTransaction(TransactionEvent transactionEvent) {
        transactionService.processTransaction(transactionEvent.toEntity());
    }
}

Notice that no further setup is necessary. Behind the scenes the framework will detect that queue type is FIFO and make all necessary adjustments to make sure the listener method receives the messages in the correct order.

6.2. Creating the Test

Let’s create a test that asserts that the order in which the messages are received is exactly the order in which were sent. We start with a test suite that extends the BaseSqsLiveTest we created earlier:

@SpringBootTest
public class SpringCloudAwsSQSTransactionProcessingTest extends BaseSqsLiveTest {
    @Autowired
    private SqsTemplate sqsTemplate;

    @Autowired
    private TransactionService transactionService;

    @Value("${events.queues.fifo.transactions-queue}")
    String transactionsQueue;

    @Test
    void givenTransactionsFromSameAccount_whenSend_shouldReceiveInOrder() {
        var accountId = UUID.randomUUID();
        var transactions = List.of(createDeposit(accountId, 100.0), 
          createWithdraw(accountId, 50.0), createDeposit(accountId, 25.0));
        var messages = createTransactionMessages(accountId, transactions);

        sqsTemplate.sendMany(transactionsQueue, messages);

        await().atMost(Duration.ofSeconds(5))
          .until(() -> transactionService.getProcessedTransactionsByAccount(accountId),
            isEqual(eventsToEntities(transactions)));
    }
}

In this test, we’re leveraging SqsTemplate‘s sendMany() method that enables us to send up to 10 messages in the same batch. We’re then waiting for up to 5 seconds to receive the messages in order.

We’ll also create a few helper methods to help us keep the test logic clean. The sendMany() method expects a List<Pojo>, so the createTransactionMessages() method maps each transaction for an accountId to a message:

private List<Message<TransactionEvent>> createTransactionMessages(UUID accountId,
        Collection<TransactionEvent> transactions) {
    return transactions.stream()
        .map(transaction -> MessageBuilder.withPayload(transaction)
          .setHeader(SqsHeaders.MessageSystemAttributes.SQS_MESSAGE_GROUP_ID_HEADER,
             accountId.toString())
          .build())
        .toList();
}

In SQS FIFO, the MessageGroupId attribute is used to inform which messages should be grouped together and received in order.  In our scenario, we must make sure that the transactions for one Account are kept in order, but we don’t need any ordering between accounts, so we’ll use the accountId as the MessageGroupId. For that we can use the headers in SqsHeaders and the framework will map them to SQS Message Attributes:

The remaining helper methods are straightforward methods for mapping events to transactions, and creating TransactionEvents:

private List<Transaction> eventsToEntities(List<TransactionEvent> transactionEvents) {
    return transactionEvents.stream()
      .map(TransactionEvent::toEntity)
      .toList();
}

private TransactionEvent createWithdraw(UUID accountId1, double amount) {
    return new TransactionEvent(UUID.randomUUID(), accountId1, amount, TransactionType.WITHDRAW);
}

private TransactionEvent createDeposit(UUID accountId1, double amount) {
    return new TransactionEvent(UUID.randomUUID(), accountId1, amount, TransactionType.DEPOSIT);
}

6.3. Running the Test

When we run the tests, we’ll see that the test passes and generates logs similar to this, with the transactions happening in the same order as we declared them:

TransactionService : Processing transaction: DEPOSIT:100.0 for account f97876f9-5ef9-4b62-a69d-a5d87b5b8e7e
TransactionService : Processing transaction: WITHDRAW:50.0 for account f97876f9-5ef9-4b62-a69d-a5d87b5b8e7e
TransactionService : Processing transaction: DEPOSIT:25.0 for account f97876f9-5ef9-4b62-a69d-a5d87b5b8e7e

If we’re still not convinced and want to make sure it’s not a coincidence, we can add the @RepeatableTest annotation to run the tests 100 times

@RepeatedTest(100)
void givenTransactionsFromSameAccount_whenSend_shouldReceiveInOrder() {
   // ...test remains the same
}

All 100 runs should equally pass with the logs in the same order.

For an extra sanity check, let’s use a standard queue rather than a FIFO and verify how it behaves.

For that, we’ll need to remove the .fifo suffix from the queue name in application.yml:

transactions-queue: "transactions-queue"

Next, we’ll comment out the code that adds the MessageId header in the createTransactionMessages() method since Standard SQS Queues do not support that attribute:

// .setHeader(SqsHeaders.MessageSystemAttributes.SQS_MESSAGE_GROUP_ID_HEADER, accountId.toString())

Now, let’s run the test 100 times again. You’ll notice that the test will sometimes pass because coincidentally the messages arrived in the expected order, but other times it’ll fail since there’s no guarantee about message ordering in a Standard Queue.

Before we wrap up this section, let’s undo these changes and add the .fifo suffix in the queue, remove the @RepeatedTest annotation, and uncomment the MessageGroupId code.

7. Processing Multiple Message Groups in Parallel

In SQS FIFO, in order to maximize message consumption throughput, we can process messages from different message groups in parallel, while keeping message order within a message group. Spring Cloud AWS SQS supports that behavior out of the box, with no further configuration needed.

To illustrate that behavior, let’s add a method to TransactionService that simulates a slow connection:

public void simulateSlowProcessing(Transaction transaction) {
    try {
        processTransaction(transaction);
        Thread.sleep(Thread.sleep(100));
        logger.info("Transaction processing completed: {}:{} for account {}",
          transaction.type(), transaction.amount(), transaction.accountId());
    } catch (InterruptedException e) {
        Thread.currentThread()
          .interrupt();
        throw new RuntimeException(e);
    }
}

The slow connection will help us assert that the messages from different accounts are being processed in parallel while preserving transaction order within each account.

Now, let’s create a listener that’ll use the new method in the TransactionListener class:

@SqsListener("${events.queues.fifo.slow-queue}")
public void processParallelTransaction(TransactionEvent transactionEvent) {
    transactionService.simulateSlowProcessing(transactionEvent.toEntity());
}

Lastly, let’s create a test to assert the behavior:

@Test
void givenTransactionsFromDifferentAccounts_whenSend_shouldProcessInParallel() {
    var accountId1 = UUID.randomUUID();
    var accountId2 = UUID.randomUUID();

    var account1Transactions = List.of(createDeposit(accountId1, 100.0),
      createWithdraw(accountId1, 50.0), createDeposit(accountId1, 25.0));
    var account2Transactions = List.of(createDeposit(accountId2, 50.0),
      createWithdraw(accountId2, 25.0), createDeposit(accountId2, 50.0));

    var allMessages = Stream.concat(createTransactionMessages(accountId1, account1Transactions).stream(),
      createTransactionMessages(accountId2, account2Transactions).stream()).toList();

    sqsTemplate.sendMany(slowQueue, allMessages);

    await().atMost(Duration.ofSeconds(5))
      .until(() -> transactionService.getProcessedTransactionsByAccount(accountId1),
        isEqual(eventsToEntities(account1Transactions)));

    await().atMost(Duration.ofSeconds(5))
      .until(() -> transactionService.getProcessedTransactionsByAccount(accountId2),
        isEqual(eventsToEntities(account2Transactions)));
}

In this test, we’re sending two sets of transaction events for two different accounts. We’re again leveraging the sendMany() method to send all messages in the same batch, and asserting the messages are being received in the expected order.

When we run the test, we should see logs similar to this:

TransactionService : Processing transaction: DEPOSIT:50.0 for account 639eba64-a40d-458a-be74-2457dff9d6d1
TransactionService : Processing transaction: DEPOSIT:100.0 for account 1a813756-520c-4713-a0ed-791b66e4551c
TransactionService : Transaction processing completed: DEPOSIT:100.0 for account 1a813756-520c-4713-a0ed-791b66e4551c
TransactionService : Transaction processing completed: DEPOSIT:50.0 for account 639eba64-a40d-458a-be74-2457dff9d6d1
TransactionService : Processing transaction: WITHDRAW:50.0 for account 1a813756-520c-4713-a0ed-791b66e4551c
TransactionService : Processing transaction: WITHDRAW:25.0 for account 639eba64-a40d-458a-be74-2457dff9d6d1
TransactionService : Transaction processing completed: WITHDRAW:50.0 for account 1a813756-520c-4713-a0ed-791b66e4551c
TransactionService : Transaction processing completed: WITHDRAW:25.0 for account 639eba64-a40d-458a-be74-2457dff9d6d1
TransactionService : Processing transaction: DEPOSIT:50.0 for account 639eba64-a40d-458a-be74-2457dff9d6d1
TransactionService : Processing transaction: DEPOSIT:25.0 for account 1a813756-520c-4713-a0ed-791b66e4551c

We can see that both accounts are being processed in parallel while keeping order within each account, which is also verified by the test passing.

8. Retrying Processing in Order

In our last scenario, we’ll simulate a network failure and ensure the processing order remains consistent. When the listener method throws an error, the framework halts execution for that message group and does not acknowledge the messages. SQS serves the remaining messages again after the visibility window has expired.

To illustrate that behavior, we’ll add a new method to our TransactionService that’ll always fail the first time it processes a message.

First, let’s add a Set to hold the IDs that have already failed:

private final Set<UUID> failedTransactions = ConcurrentHashMap.newKeySet();

Then let’s add the processTransactionWithFailure() method:

public void processTransactionWithFailure(Transaction transaction) {
    if (!failedTransactions.contains(transaction.transactionId())) {
        failedTransactions.add(transaction.transactionId());
        throw new RuntimeException("Simulated failure for transaction " +
          transaction.type() + ":" + transaction.amount());
    }
    processTransaction(transaction);
}

This method will throw an error the first time a transaction is processed but will process it normally in subsequent retries.

Now, let’s add the listener to process the messages. We’ll set messageVisibilitySeconds to one to diminish the visibility window and speed up retries in our test:

@SqsListener(value = "${events.queues.fifo.failure-queue}", messageVisibilitySeconds = "1")
public void retryFailedTransaction(TransactionEvent transactionEvent) {
    transactionService.processTransactionWithFailure(transactionEvent.toEntity());
}

Lastly, let’s create a test to assert the behavior is as expected:

@Test
void givenTransactionProcessingFailure_whenSend_shouldRetryInOrder() {
    var accountId = UUID.randomUUID();
    var transactions = List.of(createDeposit(accountId, 100.0),
      createWithdraw(accountId, 50.0), createDeposit(accountId, 25.0));
    var messages = createTransactionMessages(accountId, transactions);

    sqsTemplate.sendMany(failureQueue, messages);

    await().atMost(Duration.ofSeconds(10))
      .until(() -> transactionService.getProcessedTransactionsByAccount(accountId),
        isEqual(eventsToEntities(transactions)));
}

In this test, we’re sending three events and asserting they’re processed in the expected order.

When we run the test, we should see a log similar to this in the exception stack trace:

Caused by: java.lang.RuntimeException: Simulated failure for transaction DEPOSIT:100.0

Followed by:

TransactionService : Processing transaction: DEPOSIT:100.0 for account 3f684ccb-80e8-4e40-9136-c3b59bdd980b

Indicating the event was successfully processed on the second try.

We should see 2 similar pairs for the next events:

Caused by: java.lang.RuntimeException: Simulated failure for transaction WITHDRAW:50.0
TransactionService : Processing transaction: WITHDRAW:50.0 for account 3f684ccb-80e8-4e40-9136-c3b59bdd980b
Caused by: java.lang.RuntimeException: Simulated failure for transaction DEPOSIT:25.0
TransactionService : Processing transaction: DEPOSIT:25.0 for account 3f684ccb-80e8-4e40-9136-c3b59bdd980b

This indicates that the events were processed in the right order, even in the presence of failures.

9. Conclusion

In this article, we explored Spring Cloud AWS v3’s support for FIFO queues. We created a transaction processing service that relies on the events being processed in order and asserted message order is respected in three different scenarios: processing a single message group, processing multiple message groups in parallel, and retrying messages after a failure.

We tested each scenario by setting up a local test environment and creating live tests to assert our logic.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.

Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – Spring Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 30% off until 31st March, 2026

>> EXPLORE ACCESS NOW

Course – Spring Sale 2026 – NPI (All)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 30% off until 31st March, 2026

>> EXPLORE ACCESS NOW

eBook Jackson – NPI EA – 3 (cat = Jackson)
eBook – eBook Guide Spring Cloud – NPI (cat=Cloud/Spring Cloud)
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments