In this post under Spring Retry, I will explain how to configure Spring Retry framework into your application with simple example
At minimum, you need the below jars in your classpath
1) spring-retry-1.3.1.jar
2) commons-logging-1.2.jar
3) spring-core-4.3.22.RELEASE.jar
4) spring-aop-4.3.22.REELEASE.jar
5) spring-beans-4.3.22.RELEASE.jar
6) spring-context-4.3.22.RELEASE.jar
7) spring-expression-4.3.22.RELEASE.jar
8) aspectjweaver-1.8.9.jar
If we want to retry a particular failed operation, we can take the help of Spring Retry Framework’s org.springframework.retry.RetryOperations interface. Below are the api of this interface
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;
}
As you can see from the above javadoc, the interface has 4 overloaded versions of execute method.
Where
RetryCallback contains the logic that has to be retried. For example attempting to get database connection. If failed on first attempt retry 3 times.
RecoveryCallback contains the logic which is executed when the retry attempt has been exhausted. For example if attempting to get database connection failed after retrying 3 times, send a reference to in memory repository.
RetryState a reference to stateful retry. We will cover the purpose of this in future posts.
Spring Retry Framework provides a default implementation of RetryOperations interface called org.springframework.retry.support.RetryTemplate. We can directly use this
class instead of using our own implementation of RetryOperations interface. I didn’t see any scenario where we have to create our own implementation of RetryOperations interface. RetryTemplate class itself suffice.
For our example, we will create a service class which will contain the logic to be retried as shown below
Service1.java
public class Service1 {
private int i = 0;
private int j = 0;
public void executeWithException() {
i = i + 1;
System.out.println("Executing method 'executeWithException' : " + i);
throw new NullPointerException();
}
public void executeWithoutException() {
j = j + 1;
System.out.println("Executing method 'executeWithoutException' : " + j);
}
}
As you can see in the above code, we have two methods one which throws exception and another doesn’t.
Lets define bean definition of the above service class and RetryTemplate class in the below xml file
Example1.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
5 <bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate"/>
6
7 <bean id="service1" class="Service1"/>
8 </beans>
In the above xml code, at line 5, we create a bean definition for “RetryTemplate”. This will create an instance of RetryTemplate with default configurations. According to the default configuration, the Service1 class methods will be retried max 3 times whenever they throw an instance of Exception or any subclass of Exception class.
At line 7, we create a bean definition for “Service1” class.
Below is the main class showing how to access these bean definitions and use them.
Main class
1 import org.springframework.context.ApplicationContext;
2 import org.springframework.context.support.ClassPathXmlApplicationContext;
3 import org.springframework.retry.support.RetryTemplate;
4
5 public class Example1 {
6 public static void main(String[] args) {
7 ApplicationContext context = new ClassPathXmlApplicationContext("Example1.xml");
8 Service1 service1 = (Service1)context.getBean("service1");
9 RetryTemplate retryTemplate = (RetryTemplate)context.getBean("retryTemplate");
10
11 retryTemplate.execute(retryContext -> { service1.executeWithoutException(); return null; });
12 retryTemplate.execute(retryContext -> { service1.executeWithException(); return null; });
13 }
14 }
In the above code, as with any Spring standalone application, at first we are creating an instance of ApplicationContext using Example1.xml file at line 7.
At line 8 and 9 we retrieve an instance of Service1 and RetryTemplate class from the application context.
At line 11, we call the “execute” method of RetryTemplate. The “execute” method takes an instance of RetryCallback interface. Below is the javadoc of RetryCallback interface
public interface RetryCallback<T> {
T doWithRetry(RetryContext context) throws Throwable;
}
In the above javadoc, RetryContext is a class that stores context information that will be used by RetryTemplate to figure out whether to retry or not. We can store
other informations that we think will be used in subsequent retry attempts.
Using lambdas, at line 11, we are instructing the “retryTemplate” instance to execute “Service1” class “executeWithoutException” method and return null.
At line 12, again using lambda, we are instructing the “retryTemplate” instance to execute “Service1” class “executeWithException” method and return null.
Since “executeWithoutException” doesn’t throw any exception, it will be executed only once. Whereas “executeWithException” throws exception and it will be executed
three times. At the third attempt if exception still thrown, it will be logged in the console and the program exits.
Below is the output
Output
[INFO] ClassPathXmlApplicationContext – Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6576fe71: startup date [Sat May 29 18:12:32 IST 2021]; root of context hierarchy
[INFO] XmlBeanDefinitionReader – Loading XML bean definitions from class path resource [Example1.xml]
Executing method ‘executeWithoutException’ : 1
Executing method ‘executeWithException’ : 1
Executing method ‘executeWithException’ : 2
Executing method ‘executeWithException’ : 3
Exception in thread “main” java.lang.NullPointerException
at Service1.executeWithException(Service1.java:8)
at Example1.lambda$1(Example1.java:12)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:209)
at Example1.main(Example1.java:12)