Mastering Retry Strategies in Spring Boot 3: 5 Practical Components

This article explores the growing importance of retry mechanisms in distributed Spring Boot 3 applications, presenting five practical retry components—Spring‑Retry, Resilience4j‑Retry, Fast‑Retry, Easy‑Retry, and Guava‑Retrying—along with custom annotation examples, configuration details, code snippets, and execution results.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Retry Strategies in Spring Boot 3: 5 Practical Components

1. Introduction

In modern distributed systems and micro‑service architectures, inter‑service communication complexity leads to frequent network latency, timeouts, and transient failures such as database disconnections or third‑party API errors. To improve resilience, retry logic is often extracted from business code and implemented as a cross‑cutting concern using tools like Spring Retry or Resilience4j, which support declarative configuration, exponential back‑off, jitter, and integration with circuit breakers and rate limiters.

2. Practical Cases

2.1 Spring‑Retry

Spring‑Retry is an official Spring project that provides declarative retry support for Spring Batch, Spring Integration, and other modules.

Dependency

<dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
</dependency>

Enable Retry

@EnableRetry
public class AppConfig {}

Usage Example

@Retryable(recover = "deductStockRecover", retryFor = StockDeductException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void deductStock(Stock stock, Integer count) {
    System.out.printf("准备扣减【%s】商品, 总数: %d%n", stock.name(), count);
    if (stock.quantity() < count) {
        throw new StockDeductException("库存不足");
    }
    // ...
}

@Recover
public void deductStockRecover(StockDeductException e, Stock stock, Integer count) {
    System.err.printf("【%s】库存扣减失败, 库存不足: %d个%n", stock.name(), count);
}

Run result:

2.2 Resilience4j‑Retry

Resilience4j is a lightweight fault‑tolerance library designed for functional programming, offering retry, circuit‑breaker, rate‑limiter, bulkhead, and more.

Dependency

<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-spring-boot3</artifactId>
  <version>2.2.0</version>
</dependency>

Usage Example

@Retry(name = "stock-deduct", fallbackMethod = "stockDeductFallback")
public void deductStock(Stock stock, Integer count) {
    System.out.printf("准备扣减【%s】商品, 总数: %d%n", stock.name(), count);
    if (stock.quantity() < count) {
        throw new StockDeductException("库存不足");
    }
    // ...
}

public void stockDeductFallback(Stock stock, Integer count, Throwable e) {
    System.err.printf("resilience4j-retry, 【%s】库存扣减失败, 库存不足: %d个%n", stock.name(), count);
}

Configuration (application.yml):

resilience4j.retry:
  instances:
    stock-deduct:
      max-attempts: 3
      wait-duration: 1000
      exponential-backoff-multiplier: 1

Run result:

2.3 Fast‑Retry

Fast‑Retry is a high‑performance asynchronous retry framework capable of handling millions of concurrent tasks, supporting timeout, callbacks, and back‑off strategies.

Dependency

<dependency>
  <groupId>io.github.burukeyou</groupId>
  <artifactId>fast-retry-spring</artifactId>
  <version>0.3.2</version>
</dependency>

Enable Fast‑Retry

@EnableFastRetry
public class AppConfig {}

Usage Example

@FastRetry(
    retryWait = @RetryWait(delay = 1, timeUnit = TimeUnit.SECONDS),
    exceptionRecover = true,
    maxAttempts = 2,
    briefErrorLog = true)
public void deductStock(Stock stock, Integer count) {
    System.out.printf("准备扣减【%s】商品, 总数: %d%n", stock.name(), count);
    if (stock.quantity() < count) {
        throw new StockDeductException("库存不足");
    }
    // ...
}

Run result:

2.4 Easy‑Retry

Easy‑Retry, originally from Alibaba, provides a memory‑based persistent retry solution (now deprecated for Spring Boot 3, tested on Spring Boot 2.7).

Dependencies

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>easy-retry-memory-starter</artifactId>
  <version>1.0.3</version>
</dependency>

<dependency>
  <groupId>com.caucho</groupId>
  <artifactId>hessian</artifactId>
  <version>4.0.63</version>
</dependency>

Enable Memory‑Based Configuration

spring:
  easyretry:
    memory:
      enabled: true
      maxRetryTimes: 2

Usage Example

@EasyRetryable
public void deductStock(Stock stock, Integer count) {
    System.out.printf("准备扣减【%s】商品, 总数: %d%n", stock.getName(), count);
    if (stock.getQuantity() < count) {
        throw new StockDeductException("库存不足");
    }
    // ...
}

Run result:

2.5 Guava‑Retrying

Guava‑Retrying offers a generic retry mechanism for any Java code, enhanced with Guava predicates for stop conditions and retry strategies.

Dependency

<dependency>
  <groupId>com.github.rholder</groupId>
  <artifactId>guava-retrying</artifactId>
  <version>2.0.0</version>
</dependency>

Usage Example

private static final Retryer<Object> retryer = RetryerBuilder.<Object>newBuilder()
    .retryIfException()
    .withWaitStrategy(WaitStrategies.exponentialWait(100, 200, TimeUnit.SECONDS))
    .withStopStrategy(StopStrategies.stopAfterAttempt(3))
    .build();

public void deductStock(Stock stock, Integer count) {
    try {
        retryer.call(() -> {
            System.out.printf("准备扣减【%s】商品, 总数: %d%n", stock.name(), count);
            if (stock.quantity() < count) {
                throw new StockDeductException("库存不足");
            }
            return null;
        });
    } catch (RetryException | ExecutionException e) {
        System.err.println(e.getMessage());
    }
}

Run result:

2.6 Custom Retry Component

Define a custom annotation and AOP aspect to control retry behavior.

Custom Annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PackRetry {
    int maxAttempts() default 3;
    long retryInterval() default 1000;
    Class<? extends Throwable>[] retryFor() default {Exception.class};
}

Retry Aspect

@Aspect
@Component
public class PackRetryAspect {
    private static final int DEFAULT_MAX_RETRIES = 3;

    @Around("@annotation(packRetry)")
    public Object retryOperator(ProceedingJoinPoint pjp, PackRetry packRetry) throws Throwable {
        long interval = packRetry.retryInterval();
        Class<? extends Throwable>[] retryExceptions = packRetry.retryFor();
        int maxAttempts = packRetry.maxAttempts() + 1;
        int maxRetries = maxAttempts < 0 ? DEFAULT_MAX_RETRIES : maxAttempts;
        int numAttempts = 0;
        Throwable exception;
        do {
            numAttempts++;
            try {
                return pjp.proceed();
            } catch (Throwable ex) {
                exception = ex;
                if (!shouldRetry(ex, retryExceptions) || numAttempts >= maxAttempts) {
                    break;
                }
                retryWait(interval, numAttempts, exception);
            }
        } while (numAttempts <= maxRetries);
        throw exception;
    }

    private boolean shouldRetry(Throwable ex, Class<? extends Throwable>[] retryExceptions) {
        return Arrays.stream(retryExceptions).anyMatch(cls -> cls.isInstance(ex));
    }

    private void retryWait(long interval, int attempt, Throwable ex) {
        try {
            synchronized (this) { wait(interval); }
            System.out.printf("→ Retry #%d after %dms (cause: %s)%n", attempt, interval, ex.getClass().getSimpleName());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Retry interrupted", e);
        }
    }
}

Usage Example

@PackRetry
public void deductStock(Stock stock, Integer count) {
    System.out.printf("准备扣减【%s】商品, 总数: %d%n", stock.name(), count);
    if (stock.quantity() < count) {
        throw new StockDeductException("库存不足");
    }
}

Run result:

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootRetryGuavafast-retrySpring Retryresilience4jEasy Retry
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.