Add a possibility to use listener per @Retryable method instead of global listeners
In my project I wanted to add a listener and use it along with @Retryable annotation.
But when I add a listener, it's automatically considered by Spring Retry as global one, meaning it will be applied at least to every method marked with @Retryable annotation, which prevents me from creating listener, because I don't want to impact an existing code.
Example:
@Retryable(retryFor = SomeException.class, maxAttempts = 5, listeners = "myListenerBean")
When I create myListenerBean, it will be automatically applied to all existing methods with @Retryable annotation, which is not desired behavior.
The only way to prevent this is to either specify an empty string on existing code, which is not always possible to modify an existing code
@Retryable(listeners = "")
or use interceptor attribute which cannot be used with any other attributes and which requires interceptor bean along with all retry configurations code:
@Retryable(interceptor = "myInterceptorBean")
As a result instead of using neat single line:
@Retryable(retryFor = SomeException.class, maxAttempts = 5, listeners = "myListenerBean")
I'm forced to use:
@Retryable(interceptor = "myInterceptorBean")
along with whole retry configuration, for example:
@Slf4j
@Configuration
@RequiredArgsConstructor
public class ConnectionErrorRetryConfig {
public static final String CONNECTION_ERROR_RETRY_INTERCEPTOR = "connectionErrorRetryInterceptor";
@Value("${request.max-attempts:5}")
private int maxAttempts;
private final MyService myService;
@Bean(CONNECTION_ERROR_RETRY_INTERCEPTOR)
public RetryOperationsInterceptor connectionErrorRetryInterceptor() {
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
simpleRetryPolicy.setMaxAttempts(maxAttempts);
RetryTemplate retryTemplate = RetryTemplate.builder()
.customPolicy(simpleRetryPolicy)
.retryOn(SomeException.class)
// forced to create a new RetryListener here as NOT a bean, because bean is impacting globally every @Retryable method
.withListener(new RetryListener() {
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable)
{
if (throwable instanceof SomeException someException)
{
myService.doImportantAction(someException);
}
RetryListener.super.onError(context, callback, throwable);
}
})
.build();
return RetryInterceptorBuilder.stateless()
.retryOperations(retryTemplate)
.recoverer(new ConnectionErrorRecoverer())
.build();
}
}
As it's been already discussed here, we could think of any alternative, for example deprecating existing listeners attribute and/or providing some other attribute(s) etc. in order to not use global listeners.
Thanks.
By @artembilan we can solve this in the future: https://github.com/spring-projects/spring-retry/discussions/484#discussioncomment-12085232
Thank you, Yuriy!
Please, take into account that words starting with @ have to be present as a code (wrapped into back-ticks), otherwise GitHub treats it as a user mention.
I mean that gas to be like:
methods with
@Retryableannotation.
@artembilan sure, corrected, thx
It doesn’t matter now in this post: a notification has already been sent to that GH user 😅.
Good for that user : ), if such user exists then will learn about this issue 😅
The project goes into a maintenance mode, therefore all users should consider to migrate to Spring Framework Retry API instead: https://spring.io/blog/2025/09/09/core-spring-resilience-features.