Add reference documentation for @Retryable and @ConcurrencyLimit

See gh-35133
See gh-34529
This commit is contained in:
Juergen Hoeller
2025-07-10 17:14:08 +02:00
parent 200934c673
commit 481a74a202
2 changed files with 111 additions and 0 deletions
+1
View File
@@ -100,6 +100,7 @@
*** xref:core/aop-api/autoproxy.adoc[]
*** xref:core/aop-api/targetsource.adoc[]
*** xref:core/aop-api/extensibility.adoc[]
** xref:core/resilience.adoc[]
** xref:core/null-safety.adoc[]
** xref:core/databuffer-codec.adoc[]
** xref:core/aot.adoc[]
@@ -0,0 +1,110 @@
[[resilience]]
= Resilience Features
As of 7.0, the core Spring Framework includes a couple of common resilience features,
in particular `@Retryable` and `@ConcurrencyLimit` annotations for method invocations.
[[resilience-retryable]]
== Using `@Retryable`
`@Retryable` is a common annotation specifying retry characteristics for an individual
method (with the annotation declared at the method level), or for all proxy-invoked
methods in a given class hierarchy (with the annotation declared at the type level).
[source,java,indent=0,subs="verbatim,quotes"]
----
@Retryable
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
----
By default, the method invocation will be retried for any exception thrown: with at
most 3 retry attempts after an initial failure, and a delay of 1 second in-between.
This can be specifically adapted for every method if necessary, for example narrowing
the exceptions to retry::
[source,java,indent=0,subs="verbatim,quotes"]
----
@Retryable(MessageDeliveryException.class)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
----
Or for 5 retry attempts and an exponential back-off strategy with a bit of jitter:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Retryable(maxAttempts = 5, delay = 100, jitter = 10, multiplier = 2, maxDelay = 1000)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
----
Last but not least, `@Retryable` also works for reactive methods with a reactive
return type, decorating the pipeline with Reactor's retry capabilities:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Retryable(maxAttempts = 5, delay = 100, jitter = 10, multiplier = 2, maxDelay = 1000)
public Mono<Void> sendNotification() {
return Mono.from(...); // this raw Mono will get decorated with a retry spec
}
----
For details on the various characteristics, see the available annotation attributes
on {spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`]. Note:
There a String variants with placeholder support available for several attributes
as well, as an alternative to the specifically typed annotation attributes above.
[[resilience-concurrency]]
== Using `@ConcurrencyLimit`
`@ConcurrencyLimit` is an annotation specifying a concurrency limit for an individual
method (with the annotation declared at the method level), or for all proxy-invoked
methods in a given class hierarchy (with the annotation declared at the type level).
[source,java,indent=0,subs="verbatim,quotes"]
----
@ConcurrencyLimit(10)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
----
This is meant to protect the target resource from being accessed from too many
threads at the same time, similar to the effect of a pool size limit in case of
thread pool or of a connection pool that blocks access if its limit is reached.
At its most constrained, you may set the limit to 1, effectively locking access
to the target bean instance:
[source,java,indent=0,subs="verbatim,quotes"]
----
@ConcurrencyLimit(1) // 1 is the default but this makes it more readable
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
----
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
{spring-framework-api}/core/task/SimpleAsyncTaskExecutor.html[`SimpleAsyncTaskExecutor`].
For synchronous invocations, this annotation provides equivalent behavior through
{spring-framework-api}/aop/interceptor/ConcurrencyThrottleInterceptor.html[`ConcurrencyThrottleInterceptor`]
which is available since Spring Framework 1.0 for programmatic use with the AOP framework.
[[resilience-enable]]
== Configuring `@EnableResilientMethods`
Note that 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 of enabling actual processing of the resilience annotations
through AOP interception is to declare `@EnableResilientMethods` on a corresponding
configuration class. Alternatively, you may declare `RetryAnnotationBeanPostProcessor`
and/or `ConcurrencyLimitBeanPostProcessor` individually.