I am testing a spring retry, but it seems the recover is not being called. Tried to get it work but it seems exhaustive. I passed to @Recover no argument, Throwable, Exception. Changed retry dependency version, and it seems it is included with aop for spring boot and removed it. Kept getting recover is not being call with the following exception messege.
Request processing failed; nested exception is org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method; nested exception is java.lang.ArithmeticException: / by zero] with root cause
Any help would be much appreciated
The code i have looks like below.
Configuration class
package hello;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
@SpringBootApplication
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by `Spring Boot:");`
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
Rest Controller class;
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@Autowired
private SomeService service;
@RequestMapping("/")
public String hello() {
String result = service.getInfo();
return result;
}
}
Service class is ;
package hello;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class SomeService {
@Retryable(value = ArithmeticException.class, maxAttempts = 3, `backoff = @Backoff(delay = 3000))`
public String getInfo() {
System.out.println("How many time will this be printed?");
return "Hello" + 4/0;
}
@Recover
public void helpHere(ArithmeticException cause) {
System.out.println(cause);
System.out.println("Recovery place!");
}
}
This is my dependencies list
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- tag::actuator[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- end::actuator[] -->
<!-- tag::tests[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- end::tests[] -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
With try-catch and variety of arguments
@Service
public class SomeService {
@Retryable(value = {ArithmeticException.class}, maxAttempts = 3, `backoff = @Backoff(delay = 3000))`
public String getInfo() {
try {
System.out.println("How many time will this be printed?");
return "Hello" + 4/0;
} catch(ArithmeticException ex) {
System.out.println("In the arthemetic Exception");
throw new ArithmeticException();
}
}
@Recover
public void helpHere(ArithmeticException cause) {
System.out.println(cause);
System.out.println("Recovery place! ArithmeticException");
}
@Recover
public void helpHere(Exception cause ) {
System.out.println(cause);
System.out.println("Recovery place! Exception");
}
@Recover
public void helpHere(Throwable cause) {
System.out.println(cause);
System.out.println("Recovery place! Exception");
}
@Recover
public void helpHere() {
System.out.println("Recovery place! Exception");
}
}
Screen shot of the console
I finally got the answer.
For a method annotated with @Recover to be invoked, it has to have the same method argument(plus the exception) and the same return type.
I tested it with different type of exception argument and methods are called if they have more specific exception type. If I have a method like this will be called than one with Exception
argument. However, if I have multiple recover methods, only one with the more specific exception argument will be called.
@Recover
public String helpHere(ArithmeticException cause) {
Final code Example
package hello;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class SomeService {
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 3000))
public String getInfo() {
try {
System.out.println("How many time will this be printed?");
return "Hello" + 4/0;
} catch(Exception ex) {
System.out.println("In the arthemetic Exception");
throw new ArithmeticException();
}
}
@Recover
public String helpHere(ArithmeticException cause) {
System.out.println("Recovery place! ArithmeticException");
return "Hello";
}
@Recover
public String helpHere(Exception cause ) {
System.out.println("Recovery place! Exception");
return "Hello";
}
@Recover
public String helpHere() {
System.out.println("Recovery place! Exception");
return "Hello";
}
@Recover
public String helpHere(Throwable cause) {
System.out.println("Recovery place! Throwable");
return "Hello";
}
You should use try-catch
to handle it. Here the example
@Retryable(value = ArithmeticException.class, maxAttempts = 5, backoff = @Backoff(delay = 3000))
public String getInfo() {
try {
System.out.println("How many time will this be printed?");
return "Hello" + 4 / 0;
} catch (ArithmeticException ex) {
// will be retried
throw ex;
}
}
throw ex;
is a must as it is telling Spring to apply retry handling.
With @Recover
we define a separate recovery method for ArithmeticException
. This allows us to run special recovery code when a retryable method fails with ArithmeticException
.
You may refer more on How to handle retry with Spring-Retry ?
Edit
Based on the latest exception,try provide version for spring-retry
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With