In a project, there might be a use case where you need to re-invoke a failed operation or call a method again on failure.

Spring Retry provides declarative retry support for Spring applications.

In this blog, we will configure and use Spring Retry logic using Spring Application.

Install Dependencies




	compile group: 'org.springframework.retry', name: 'spring-retry', version: '1.2.2.RELEASE'

Link for other versions of spring-retry.

Please Note: You will also need dependent jars for Spring Retry.



1. Enable Spring Retry in SpringBoot Application

@EnableRetry // Mandatory without this @Retryable logic will not work
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);

2. Use retry logic on a method using annotation.

@Retryable: To add retry functionality to methods, @Retryable can be used.
@Recover: The @Recover annotation is used to define a recovery method when a @Retryable method fails with a specified exception.

import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

public class TestRetry {

	@Retryable(value = { ArithmeticException.class }, maxAttempts = 4, backoff = @Backoff(500))
	public String loadData(@RequestParam("id") int id) {
		System.out.println("loadData method called "+(new Date()).getTime());
		if (id % 2 == 0) {
			int a = id/0; // just to generate exception
		return "Success";

	public String recover(ArithmeticException t, int d) {
		System.out.println("recover method called");
		return "Error";



loadData method called 1542818097643
loadData method called 1542818098148
loadData method called 1542818098648
loadData method called 1542818099153
recover method called

Points to Note

  1. Retry will be called only when ArithmeticException is thrown from the method block in the above example.
  2. Method will retry max 4 times [maxAttempts = 4] with a delay of 500ms [backoff = @Backoff(500))]
  3. @Retryable without any attribute will try 3 times with a delay of 1 sec if the method fails with an exception.
  4. @Recover is called when the @Retryable method fails ie after trying maxAttempts times.
  5. In @Recover the return type must match the @Retryable method.
  6. The arguments for the recovery method can optionally include the exception that was thrown, and also optionally the arguments passed to the original retryable method (or a partial list of them as long as none are omitted).
  7. For a random backoff between 100 and 500 milliseconds. @Retryable(maxAttempts=12, backoff=@Backoff(delay=100, maxDelay=500))

Few Pitfall

1. If the return type is not matched you get the below-mentioned error. For example, if you’re recover block code is

// If you try this block as Recover in above code
public void recover() { // Notice return type
	System.out.println("recover method called");


nested exception is org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method; nested exception is 
java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
at com.retry.demo.controller.TestRetry.loadData(TestRetry.java:20) ~[classes/:na]
at com.retry.demo.controller.TestRetry$$FastClassBySpringCGLIB$$a2b003c.invoke(<generated>) ~[classes/:na]


2. If the number of arguments exceeds in recover method you will get below mentioned error. For example, if you’re recover block code is

public String recover(int a, int b) { // parameter exceed
	System.out.println("recover method called");
	return "Error";


threw exception [Request processing failed; nested exception is java.lang.ArrayIndexOutOfBoundsException] with root cause

java.lang.ArrayIndexOutOfBoundsException: null
at java.lang.System.arraycopy(Native Method) ~[na:1.8.0_40]
at org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler$SimpleMetadata.getArgs(RecoverAnnotationRecoveryHandler.java:180) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler.recover(RecoverAnnotationRecoveryHandler.java:63) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.interceptor.RetryOperationsInterceptor$ItemRecovererCallback.recover(RetryOperationsInterceptor.java:141) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.support.RetryTemplate.handleRetryExhausted(RetryTemplate.java:512) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:351) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:180) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:115) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:153) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.2.RELEASE.jar:5.1.2.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.1.2.RELEASE.jar:5.1.2.RELEAS

Source Code

Download the Sample Project used for this Blog.


Categories: JAVA


Ramanamurty · September 20, 2019 at 9:22 pm

After API explanation Pitfalls helped me to recover my issues. Thanks for the Article.

Rishab · October 18, 2019 at 2:40 pm

Sample project just works fine.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.