11[[resilience]]
22= Resilience Features
33
4- As of 7.0, the core Spring Framework includes a couple of common resilience features,
5- in particular `@Retryable` and `@ConcurrencyLimit` annotations for method invocations.
4+ As of 7.0, the core Spring Framework includes common resilience features, in particular
5+ <<resilience-annotations-retryable>> and <<resilience-annotations-concurrencylimit>>
6+ annotations for method invocations as well as <<resilience-programmatic-retry,
7+ programmatic retry support>>.
68
79
8- [[resilience-retryable]]
9- == Using `@Retryable`
10+ [[resilience-annotations- retryable]]
11+ == `@Retryable`
1012
11- `@Retryable` is a common annotation that specifies retry characteristics for an individual
12- method (with the annotation declared at the method level), or for all proxy-invoked
13- methods in a given class hierarchy (with the annotation declared at the type level).
13+ {spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`] is an annotation
14+ that specifies retry characteristics for an individual method (with the annotation
15+ declared at the method level), or for all proxy-invoked methods in a given class hierarchy
16+ (with the annotation declared at the type level).
1417
1518[source,java,indent=0,subs="verbatim,quotes"]
1619----
@@ -20,8 +23,8 @@ public void sendNotification() {
2023}
2124----
2225
23- By default, the method invocation will be retried for any exception thrown: with at
24- most 3 retry attempts after an initial failure, and a delay of 1 second between attempts.
26+ By default, the method invocation will be retried for any exception thrown: with at most 3
27+ retry attempts after an initial failure, and a delay of 1 second between attempts.
2528
2629This can be specifically adapted for every method if necessary – for example, by narrowing
2730the exceptions to retry:
@@ -38,38 +41,45 @@ Or for 5 retry attempts and an exponential back-off strategy with a bit of jitte
3841
3942[source,java,indent=0,subs="verbatim,quotes"]
4043----
41- @Retryable(maxAttempts = 5, delay = 100, jitter = 10, multiplier = 2, maxDelay = 1000)
44+ @Retryable(
45+ includes = MessageDeliveryException.class,
46+ maxAttempts = 5,
47+ delay = 100,
48+ jitter = 10,
49+ multiplier = 2,
50+ maxDelay = 1000)
4251public void sendNotification() {
4352 this.jmsClient.destination("notifications").send(...);
4453}
4554----
4655
47- Last but not least, `@Retryable` also works for reactive methods with a reactive
48- return type, decorating the pipeline with Reactor's retry capabilities:
56+ Last but not least, `@Retryable` also works for reactive methods with a reactive return
57+ type, decorating the pipeline with Reactor's retry capabilities:
4958
5059[source,java,indent=0,subs="verbatim,quotes"]
5160----
52- @Retryable(maxAttempts = 5, delay = 100, jitter = 10, multiplier = 2, maxDelay = 1000 )
61+ @Retryable(maxAttempts = 5, delay = 100)
5362public Mono<Void> sendNotification() {
5463 return Mono.from(...); // <1>
5564}
5665----
5766<1> This raw `Mono` will get decorated with a retry spec.
5867
59- For details on the various characteristics, see the available annotation attributes
60- in {spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`].
68+ For details on the various characteristics, see the available annotation attributes in
69+ {spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`].
6170
62- NOTE: There a `String` variants with placeholder support available for several attributes
63- as well, as an alternative to the specifically typed annotation attributes used in the
64- above examples.
71+ NOTE: There are `String` variants with placeholder support available for several
72+ attributes as well, as an alternative to the specifically typed annotation attributes used
73+ in the above examples.
6574
6675
67- [[resilience-concurrency ]]
68- == Using `@ConcurrencyLimit`
76+ [[resilience-annotations-concurrencylimit ]]
77+ == `@ConcurrencyLimit`
6978
70- `@ConcurrencyLimit` is an annotation that specifies a concurrency limit for an individual
71- method (with the annotation declared at the method level), or for all proxy-invoked
72- methods in a given class hierarchy (with the annotation declared at the type level).
79+ {spring-framework-api}/resilience/annotation/ConcurrencyLimit.html[`@ConcurrencyLimit`] is
80+ an annotation that specifies a concurrency limit for an individual method (with the
81+ annotation declared at the method level), or for all proxy-invoked methods in a given
82+ class hierarchy (with the annotation declared at the type level).
7383
7484[source,java,indent=0,subs="verbatim,quotes"]
7585----
@@ -95,21 +105,126 @@ public void sendNotification() {
95105----
96106<1> 1 is the default, but specifying it makes the intent clearer.
97107
98- Such limiting is particularly useful with Virtual Threads where there is generally
99- no thread pool limit in place. For asynchronous tasks, this can be constrained on
108+ Such limiting is particularly useful with Virtual Threads where there is generally no
109+ thread pool limit in place. For asynchronous tasks, this can be constrained on
100110{spring-framework-api}/core/task/SimpleAsyncTaskExecutor.html[`SimpleAsyncTaskExecutor`].
101111For synchronous invocations, this annotation provides equivalent behavior through
102112{spring-framework-api}/aop/interceptor/ConcurrencyThrottleInterceptor.html[`ConcurrencyThrottleInterceptor`]
103113which has been available since Spring Framework 1.0 for programmatic use with the AOP
104114framework.
105115
106116
107- [[resilience-enable ]]
108- == Configuring `@EnableResilientMethods`
117+ [[resilience-annotations-configuration ]]
118+ == Enabling Resilient Methods
109119
110- Note that like many of Spring's core annotation-based features, `@Retryable` and
111- `@ConcurrencyLimit` are designed as metadata that you can choose to honor or ignore.
112- The most convenient way to enable actual processing of the resilience annotations
113- through AOP interception is to declare `@EnableResilientMethods` on a corresponding
114- configuration class. Alternatively, you may declare `RetryAnnotationBeanPostProcessor`
115- and/or `ConcurrencyLimitBeanPostProcessor` individually.
120+ Like many of Spring's core annotation-based features, `@Retryable` and `@ConcurrencyLimit`
121+ are designed as metadata that you can choose to honor or ignore. The most convenient way
122+ to enable processing of the resilience annotations is to declare
123+ {spring-framework-api}/resilience/annotation/EnableResilientMethods.html[`@EnableResilientMethods`]
124+ on a corresponding `@Configuration` class.
125+
126+ Alternatively, these annotations can be individually enabled by defining a
127+ `RetryAnnotationBeanPostProcessor` or a `ConcurrencyLimitBeanPostProcessor` bean in the
128+ context.
129+
130+
131+ [[resilience-programmatic-retry]]
132+ == Programmatic Retry Support
133+
134+ In contrast to <<resilience-annotations-retryable>> which provides a declarative approach
135+ for specifying retry semantics for methods within beans registered in the
136+ `ApplicationContext`,
137+ {spring-framework-api}/core/retry/RetryTemplate.html[`RetryTemplate`] provides a
138+ programmatic API for retrying arbitrary blocks of code.
139+
140+ Specifically, a `RetryTemplate` executes and potentially retries a
141+ {spring-framework-api}/core/retry/Retryable.html[`Retryable`] operation based on a
142+ configured {spring-framework-api}/core/retry/RetryPolicy.html[`RetryPolicy`].
143+
144+ [source,java,indent=0,subs="verbatim,quotes"]
145+ ----
146+ var retryTemplate = new RetryTemplate(); // <1>
147+
148+ retryTemplate.execute(
149+ () -> jmsClient.destination("notifications").send(...));
150+ ----
151+ <1> Implicitly uses `RetryPolicy.withDefaults()`.
152+
153+ By default, a retryable operation will be retried for any exception thrown: with at most 3
154+ retry attempts after an initial failure, and a delay of 1 second between attempts.
155+
156+ If you only need to customize the number of retry attempts, you can use the
157+ `RetryPolicy.withMaxAttempts()` factory method as demonstrated below.
158+
159+ [source,java,indent=0,subs="verbatim,quotes"]
160+ ----
161+ var retryTemplate = new RetryTemplate(RetryPolicy.withMaxAttempts(5)); // <1>
162+
163+ retryTemplate.execute(
164+ () -> jmsClient.destination("notifications").send(...));
165+ ----
166+ <1> Explicitly uses `RetryPolicy.withMaxAttempts(5)`.
167+
168+ If you need to narrow the types of exceptions to retry, that can be achieved via the
169+ `includes()` and `excludes()` builder methods.
170+
171+ [source,java,indent=0,subs="verbatim,quotes"]
172+ ----
173+ var retryPolicy = RetryPolicy.builder()
174+ .includes(MessageDeliveryException.class) // <1>
175+ .excludes(...) // <2>
176+ .build();
177+
178+ var retryTemplate = new RetryTemplate(retryPolicy);
179+
180+ retryTemplate.execute(
181+ () -> jmsClient.destination("notifications").send(...));
182+ ----
183+ <1> Specify one or more exception types to include.
184+ <2> Specify one or more exception types to exclude.
185+
186+ [TIP]
187+ ====
188+ For advanced use cases, you can specify a custom `Predicate<Throwable>` via the
189+ `predicate()` method in the `RetryPolicy.Builder`, and the predicate will be used to
190+ determine whether to retry a failed operation based on a given `Throwable` – for example,
191+ by checking the cause or the message of the `Throwable`.
192+
193+ Custom predicates can be combined with `includes` and `excludes`; however, custom
194+ predicates will always be applied after `includes` and `excludes` have been applied.
195+ ====
196+
197+ The following example demonstrates how to configure a `RetryPolicy` with 5 retry attempts
198+ and an exponential back-off strategy with a bit of jitter.
199+
200+ [source,java,indent=0,subs="verbatim,quotes"]
201+ ----
202+ var retryPolicy = RetryPolicy.builder()
203+ .includes(MessageDeliveryException.class)
204+ .maxAttempts(5)
205+ .delay(Duration.ofMillis(100))
206+ .jitter(Duration.ofMillis(10))
207+ .multiplier(2)
208+ .maxDelay(Duration.ofSeconds(1))
209+ .build();
210+
211+ var retryTemplate = new RetryTemplate(retryPolicy);
212+
213+ retryTemplate.execute(
214+ () -> jmsClient.destination("notifications").send(...));
215+ ----
216+
217+ [TIP]
218+ ====
219+ A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
220+ with a `RetryTemplate` to react to events published during key retry phases (before a
221+ retry attempt, after a retry attempt, etc.), and you can compose multiple listeners via a
222+ {spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
223+ ====
224+
225+ Although the factory methods and builder API for `RetryPolicy` cover most common
226+ configuration scenarios, you can implement a custom `RetryPolicy` for complete control
227+ over the types of exceptions that should trigger a retry as well as the
228+ {spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that you
229+ can also configure a customized `BackOff` strategy via the `backOff()` method in the
230+ `RetryPolicy.Builder`.
0 commit comments