Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
S

spring-retry

> 编程语言
Open source

Spring Retry is no longer maintained as an open-source project. See resilence features blog post and reference documentation for more infor…

2.3K stars0 likes0 views
WebsiteGitHub

About

Spring Retry is no longer maintained as an open-source project. See resilence features blog post and reference documentation for more infor…

Spring Retry is no longer maintained as an open-source project.

See resilence features blog post and reference documentation for more information.


⚠️ Project Status: Archived
This project has been superseded by Spring Framework 7 and is now archived.
Please migrate to Spring Framework's resilience features.


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 Maven artifact for this library is:

xml
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

Quick Start

This section provides a quick introduction to getting started with Spring Retry. It includes a declarative example and an imperative example.

Declarative Example

The following example shows how to use Spring Retry in its declarative style:

java
@Configuration
@EnableRetry
public class Application {

}

@Service
class Service {
    @Retryable(retryFor = 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.

Imperative Example

The following example shows how to use Spring Retry in its imperative style (available since version 1.3):

java
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.

Building

Spring Retry requires Java 17 and Maven 3.0.5 (or greater). To build, run the following Maven command:

$ mvn install

Features and API

This section discusses the features of Spring Retry and shows how to use its API.

Using RetryTemplate

To 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:

java
public interface RetryOperations {

	<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E;

	<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback) throws E;

	<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RetryState retryState) throws E, ExhaustedRetryException;

	<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState retryState) throws E;

}

The basic callback is a simple interface that lets you insert some business logic to be retried:

java
public interface RetryCallback<T, E extends Throwable> {

    T doWithRetry(RetryContext context) throws E;

}

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:

java
RetryTemplate template = new RetryTemplate();

TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);

template.setRetryPolicy(policy);

MyObject result = template.execute(new RetryCallback<MyObject, Exception>() {

    public MyObject 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:

java
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();

Using RetryContext

The 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. It also has some useful properties, such as retryCount.

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.

If you don't have access to the context directly, you can obtain the current context within the scope of the retries by calling RetrySynchronizationManager.getContext(). By default, the context is stored in a ThreadLocal. JEP 444 recommends that ThreadLocal should be avoided when using virtual threads, available in Java 21 and beyond. To store the contexts in a Map instead of a ThreadLocal, call RetrySynchronizationManager.setUseThreadLocal(false).

Also, the RetryOperationsInterceptor exposes RetryOperationsInterceptor.METHOD and RetryOperationsInterceptor.METHOD_ARGS attributes with MethodInvocation.getMethod() and new Args(invocation.getArguments()) values, respectively, into the RetryContext.

Using RecoveryCallback

When 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:

MyObject myObject = template.execute(new RetryCallback<MyObject, Exception>() {
    public MyObject doWithRetry(RetryContext context) {
        // business logic here
    },
  new RecoveryCallback<MyObject>() {
    MyObject 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.

Stateless Retry

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.

Stateful 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.

In these cases, a stateless retry is not good enough, because the re-throw and roll back necessarily involve leaving the RetryOperations.execute() method and potentially losing the context that was on the stack. To avoid losing the context, we have to introduce a storage strategy to lift it off the stack and put it (at a minimum) in heap storage. For this purpose, Spring Retry provides a storage strategy called RetryContextCache, which you can inject into the RetryTemplate. The default implementation of the RetryContextCache is in-memory, using a simple Map. It has a strictly enforced maximum capacity, to avoid memory leaks, but it does not have any advanced cache features (such as time to live). You should consider injecting a Map that has those features if you need them. For advanced usage with multiple processes in a clustered environment, you might also consider implementing the RetryContextCache with a cluster cache of some sort (though, even in a clustered environment, this might be overkill).

Part of the responsibility of the RetryOperations is to recognize the failed operations when they come back in a new execution (and usually wrapped in a new transaction). To facilitate this, Spring Retry provides the RetryState abstraction. This works in conjunction with special execute methods in the RetryOperations.

The failed operations are recognized by identifying the state across multiple invocations of the retry. To identify the state, you can provide a RetryState object that is responsible for returning a unique key that identifies the item. The identifier is used as a key in the RetryContextCache.

Warning: Be very careful with the implementation of Object.equals() and Object.hashCode() in the key returned by RetryState. The best advice is to use a business key to identify the items. In the case of a JMS message, you can use the message ID.

When the retry is exhausted, you also have the option to handle the failed item in a different way, instead of calling the RetryCallback (which is now presumed to be likely to fail). As in the stateless case, this option is provided by the RecoveryCallback, which you can provide by passing it in to the execute method of RetryOperations.

The decision to retry or not is actually delegated to a regular RetryPolicy, so the usual concerns about limits and timeouts can be injected there (see the Additional Dependencies section).

Retry Policies

Inside a RetryTemplate, the decision to retry or fail in the execute method is determined by a RetryPolicy, which is also a factory for the RetryContext. The RetryTemplate is responsible for using the current policy to create a RetryContext and passing that in to the RetryCallback at every attempt. After a callback fails, the RetryTemplate has to make a call to the RetryPolicy to ask it to update its state (which is stored in RetryContext). It then asks the policy if another attempt can be made. If another attempt cannot be made (for example, because a limit has been reached o

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Java

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言