このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Framework 6.2.11 を使用してください!

レジリエンス機能

As of 7.0, the core Spring Framework includes common resilience features, in particular @Retryable and @ConcurrencyLimit annotations for method invocations as well as programmatic retry support.

@Retryable

@Retryable (Javadoc) は、個々のメソッド (メソッドレベルで宣言されたアノテーションを使用)、または特定のクラス階層内のすべてのプロキシ呼び出しメソッド (型レベルで宣言されたアノテーションを使用) の再試行特性を指定するアノテーションです。

@Retryable
public void sendNotification() {
    this.jmsClient.destination("notifications").send(...);
}

デフォルトでは、例外がスローされるたびにメソッド呼び出しが再試行されます。最初の失敗後、最大 3 回の再試行が行われ、試行間の遅延は 1 秒になります。

必要に応じて、これを各メソッドに具体的に適応させることができます。たとえば、再試行の例外を絞り込むことができます。

@Retryable(MessageDeliveryException.class)
public void sendNotification() {
    this.jmsClient.destination("notifications").send(...);
}

または、5 回の再試行と、若干のジッターを伴う指数バックオフ戦略の場合:

@Retryable(
  includes = MessageDeliveryException.class,
  maxAttempts = 5,
  delay = 100,
  jitter = 10,
  multiplier = 2,
  maxDelay = 1000)
public void sendNotification() {
    this.jmsClient.destination("notifications").send(...);
}

最後になりましたが、@Retryable はリアクティブ戻り型を持つリアクティブメソッドでも機能し、パイプラインを Reactor の再試行機能で装飾します。

@Retryable(maxAttempts = 5, delay = 100)
public Mono<Void> sendNotification() {
    return Mono.from(...); (1)
}
1 この生の Mono は再試行仕様で装飾されます。

さまざまな特性の詳細については、@Retryable (Javadoc) で使用可能なアノテーション属性を参照してください。

上記の例で使用されている特別に型指定されたアノテーション属性の代替として、いくつかの属性で使用できるプレースホルダーサポートを備えた String バリアントもあります。

@ConcurrencyLimit

@ConcurrencyLimit (Javadoc) は、個々のメソッド (メソッドレベルで宣言されたアノテーションを使用)、または特定のクラス階層内のすべてのプロキシ呼び出しメソッド (型レベルで宣言されたアノテーションを使用) の同時実行制限を指定するアノテーションです。

@ConcurrencyLimit(10)
public void sendNotification() {
    this.jmsClient.destination("notifications").send(...);
}

これは、制限に達した場合にアクセスをブロックするスレッドプールまたは接続プールのプールサイズ制限の効果と同様に、同時に多数のスレッドからターゲットリソースへのアクセスが防止されることを目的としています。

オプションで制限を 1 に設定して、ターゲットの Bean インスタンスへのアクセスを効果的にロックすることもできます。

@ConcurrencyLimit(1) (1)
public void sendNotification() {
    this.jmsClient.destination("notifications").send(...);
}
11 がデフォルトですが、これを指定すると意図が明確になります。

Such limiting is particularly useful with Virtual Threads where there is generally no thread pool limit in place. For asynchronous tasks, this can be constrained on SimpleAsyncTaskExecutor (Javadoc) . For synchronous invocations, this annotation provides equivalent behavior through ConcurrencyThrottleInterceptor (Javadoc) which has been available since Spring Framework 1.0 for programmatic use with the AOP framework.

Enabling Resilient Methods

Like many of Spring’s core annotation-based features, @Retryable and @ConcurrencyLimit are designed as metadata that you can choose to honor or ignore. The most convenient way to enable processing of the resilience annotations is to declare @EnableResilientMethods (Javadoc) on a corresponding @Configuration class.

Alternatively, these annotations can be individually enabled by defining a RetryAnnotationBeanPostProcessor or a ConcurrencyLimitBeanPostProcessor bean in the context.

Programmatic Retry Support

In contrast to @Retryable which provides a declarative approach for specifying retry semantics for methods within beans registered in the ApplicationContext, RetryTemplate (Javadoc) provides a programmatic API for retrying arbitrary blocks of code.

Specifically, a RetryTemplate executes and potentially retries a Retryable (Javadoc) operation based on a configured RetryPolicy (Javadoc)

var retryTemplate = new RetryTemplate(); (1)

retryTemplate.execute(
        () -> jmsClient.destination("notifications").send(...));
1Implicitly uses RetryPolicy.withDefaults().

By default, a retryable operation will be retried for any exception thrown: with at most 3 retry attempts after an initial failure, and a delay of 1 second between attempts.

If you only need to customize the number of retry attempts, you can use the RetryPolicy.withMaxAttempts() factory method as demonstrated below.

var retryTemplate = new RetryTemplate(RetryPolicy.withMaxAttempts(5)); (1)

retryTemplate.execute(
        () -> jmsClient.destination("notifications").send(...));
1Explicitly uses RetryPolicy.withMaxAttempts(5).

If you need to narrow the types of exceptions to retry, that can be achieved via the includes() and excludes() builder methods.

var retryPolicy = RetryPolicy.builder()
        .includes(MessageDeliveryException.class) (1)
        .excludes(...) (2)
        .build();

var retryTemplate = new RetryTemplate(retryPolicy);

retryTemplate.execute(
        () -> jmsClient.destination("notifications").send(...));
1Specify one or more exception types to include.
2Specify one or more exception types to exclude.

For advanced use cases, you can specify a custom Predicate<Throwable> via the predicate() method in the RetryPolicy.Builder, and the predicate will be used to determine whether to retry a failed operation based on a given Throwable – for example, by checking the cause or the message of the Throwable.

Custom predicates can be combined with includes and excludes; however, custom predicates will always be applied after includes and excludes have been applied.

The following example demonstrates how to configure a RetryPolicy with 5 retry attempts and an exponential back-off strategy with a bit of jitter.

var retryPolicy = RetryPolicy.builder()
        .includes(MessageDeliveryException.class)
        .maxAttempts(5)
        .delay(Duration.ofMillis(100))
        .jitter(Duration.ofMillis(10))
        .multiplier(2)
        .maxDelay(Duration.ofSeconds(1))
        .build();

var retryTemplate = new RetryTemplate(retryPolicy);

retryTemplate.execute(
        () -> jmsClient.destination("notifications").send(...));

A RetryListener (Javadoc) can be registered with a RetryTemplate to react to events published during key retry phases (before a retry attempt, after a retry attempt, etc.), and you can compose multiple listeners via a CompositeRetryListener (Javadoc)

Although the factory methods and builder API for RetryPolicy cover most common configuration scenarios, you can implement a custom RetryPolicy for complete control over the types of exceptions that should trigger a retry as well as the BackOff (Javadoc) strategy to use. Note that you can also configure a customized BackOff strategy via the backOff() method in the RetryPolicy.Builder.