
spring retry 1.3.x fix with niche toolkit for CVE-2026-41710
This project provides declarative retry support for Spring applications. It is used in Spring Batch, Spring Integration, and others. Imperative retry is also supported for explicit usage.
The version fix spring-retry with 2.0.13 for java8 and spring4 or spring5, For more information, please refer to the official Spring 2.0.13 version
This section provides a quick introduction to getting started with Spring Retry. It includes a declarative example and an imperative example.
The following example shows how to use Spring Retry in its declarative style:
@Configuration
@EnableRetry
public class Application {
@Bean
public Service service() {
return new Service();
}
}
@Service
class Service {
@Retryable(RemoteAccessException.class)
public void service() {
// ... do something
}
@Recover
public void recover(RemoteAccessException e) {
// ... panic
}
}
This example calls the service method and, if it fails with a RemoteAccessException, retries
(by default, up to three times), and then tries the recover method if unsuccessful.
There are various options in the @Retryable annotation attributes for including and
excluding exception types, limiting the number of retries, and setting the policy for backoff.
The declarative approach to applying retry handling by using the @Retryable annotation shown earlier has an additional
runtime dependency on AOP classes. For details on how to resolve this dependency in your project, see the
'Java Configuration for Retry Proxies' section.
The following example shows how to use Spring Retry in its imperative style (available since version 1.3):
RetryTemplate template = RetryTemplate.builder()
.maxAttempts(3)
.fixedBackoff(1000)
.retryOn(RemoteAccessException.class)
.build();
template.execute(ctx -> {
// ... do something
});
For versions prior to 1.3, see the examples in the RetryTemplate section.
Spring Retry requires Java 1.7 and Maven 3.0.5 (or greater). To build, run the following Maven command:
$ mvn install
This section discusses the features of Spring Retry and shows how to use its API.
RetryTemplateTo make processing more robust and less prone to failure, it sometimes helps to
automatically retry a failed operation, in case it might succeed on a subsequent attempt.
Errors that are susceptible to this kind of treatment are transient in nature. For
example, a remote call to a web service or an RMI service that fails because of a network
glitch or a DeadLockLoserException in a database update may resolve itself after a
short wait. To automate the retry of such operations, Spring Retry has the
RetryOperations strategy. The RetryOperations interface definition follows:
public interface RetryOperations {
<T> T execute(RetryCallback<T> retryCallback) throws Exception;
<T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback)
throws Exception;
<T> T execute(RetryCallback<T> retryCallback, RetryState retryState)
throws Exception, ExhaustedRetryException;
<T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
RetryState retryState) throws Exception;
}
The basic callback is a simple interface that lets you insert some business logic to be retried:
public interface RetryCallback<T> {
T doWithRetry(RetryContext context) throws Throwable;
}
The callback is tried, and, if it fails (by throwing an Exception), it is retried
until either it is successful or the implementation decides to abort. There are a number
of overloaded execute methods in the RetryOperations interface, to deal with various
use cases for recovery when all retry attempts are exhausted and to deal with retry state, which
lets clients and implementations store information between calls (more on this later).
The simplest general purpose implementation of RetryOperations is RetryTemplate.
The following example shows how to use it:
RetryTemplate template = new RetryTemplate();
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);
template.setRetryPolicy(policy);
Foo result = template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// Do stuff that might fail, e.g. webservice operation
return result;
}
});
In the preceding example, we execute a web service call and return the result to the user. If that call fails, it is retried until a timeout is reached.
Since version 1.3, fluent configuration of RetryTemplate is also available, as follows:
RetryTemplate.builder()
.maxAttempts(10)
.exponentialBackoff(100, 2, 10000)
.retryOn(IOException.class)
.traversingCauses()
.build();
RetryTemplate.builder()
.fixedBackoff(10)
.withinMillis(3000)
.build();
RetryTemplate.builder()
.infiniteRetry()
.retryOn(IOException.class)
.uniformRandomBackoff(1000, 3000)
.build();
RetryContextThe method parameter for the RetryCallback is a RetryContext. Many callbacks ignore
the context. However, if necessary, you can use it as an attribute bag to store data for
the duration of the iteration.
A RetryContext has a parent context if there is a nested retry in progress in the same
thread. The parent context is occasionally useful for storing data that needs to be shared
between calls to execute.
RecoveryCallbackWhen a retry is exhausted, the RetryOperations can pass control to a different
callback: RecoveryCallback. To use this feature, clients can pass in the callbacks
together to the same method, as the following example shows:
Foo foo = template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// business logic here
},
new RecoveryCallback<Foo>() {
Foo recover(RetryContext context) throws Exception {
// recover logic here
}
});
If the business logic does not succeed before the template decides to abort, the client is given the chance to do some alternate processing through the recovery callback.
In the simplest case, a retry is just a while loop: the RetryTemplate can keep trying
until it either succeeds or fails. The RetryContext contains some state to determine
whether to retry or abort. However, this state is on the stack, and there is no need to
store it anywhere globally. Consequently, we call this "stateless retry". The distinction
between stateless and stateful retry is contained in the implementation of RetryPolicy
(RetryTemplate can handle both). In a stateless retry, the callback is always executed
in the same thread as when it failed on retry.
Where the failure has caused a transactional resource to become invalid, there are some special considerations. This does not apply to a simple remote call, because there is (usually) no transactional resource, but it does sometimes apply to a database update, especially when using Hibernate. In this case, it only makes sense to rethrow the exception that called the failure immediately so that the transaction can roll back and we can start a new (and valid) one.