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