--Write a sample program and check the behavior of Spring Boot + Spring Retry --Spring Retry eliminates the need to write code to determine and control processing retries --Describe the number of trials and interval time in @Retryable annotation
├── build.gradle
├── settings.gradle
└── src
└── main
└── java
└── info
└── maigo
└── lab
└── sample
└── springretry
├── Application.java
├── HogeController.java
├── HogeService.java
├── KotsuException.java
├── PonException.java
├── PonKotsuRepository.java
└── PonKotsuRetryListener.java
--Return a message in JSON for HTTP request. --PonKotsuRepository is a pompous repository. It returns a message with a certain probability, but most fail. --HogeService will call PonKotsuRepository again and retry if PonKotsuRepository fails. --Write the number of trials and interval time in the @Retryable annotation. --Give up after failing a certain number of times. --PonKotsuRepository returns PonException or KotsuException on failure. --If the last failure is PonException, handle it with the specified method with @Recover annotation of Spring Retry. --If the last failure is KotsuException, handle it with the specified method with @ExceptionHandler annotation of Spring Retry. --A class that implements the RetryListener interface, and outputs the status of retry processing to the standard output.
build.gradle
Add implementation'org.springframework.retry: spring-retry: 1.2.4.RELEASE' to dependencies to use Spring Retry. Also, add runtime'org.springframework.boot: spring-boot-starter-aop' because you need the AOP class at runtime. How to specify dependent libraries can be found in GitHub -spring \ -projects / spring \ -retry.
plugins {
// The Java Plugin
// https://docs.gradle.org/current/userguide/java_plugin.html
id 'java'
// Gradle - Plugin: org.springframework.boot
// https://plugins.gradle.org/plugin/org.springframework.boot
id 'org.springframework.boot' version '2.2.0.M4'
// Gradle - Plugin: io.spring.dependency-management
/// https://plugins.gradle.org/plugin/io.spring.dependency-management
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
}
group = 'info.maigo.lab'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 11
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:2.2.0.M4'
implementation 'org.springframework.retry:spring-retry:1.2.4.RELEASE'
runtime 'org.springframework.boot:spring-boot-starter-aop'
}
settings.gradle
pluginManagement {
repositories {
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
gradlePluginPortal()
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == 'org.springframework.boot') {
useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}")
}
}
}
}
rootProject.name = 'sample.springretry'
Generate a JAR file with the build task in Gradle's Java plugin.
$ gradle build
BUILD SUCCESSFUL in 3s
3 actionable tasks: 3 executed
Execute the generated jar file with the java command. This will start the web server with Spring Boot.
$ java -jar build/libs/sample.springretry-0.0.1-SNAPSHOT.jar
Application.java
Boot entry class by Spring Boot. The @EnableRetry annotation is specified to use Spring Retry.
package info.maigo.lab.sample.springretry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
HogeController.java
A Controller class that receives an HTTP request and returns a response. When an exception occurs, the result of processing by the method with @ExceptionHandler annotation is returned.
package info.maigo.lab.sample.springretry;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HogeController {
@Autowired
private HogeService hogeService;
@RequestMapping("/{message}")
public Map<String, Object> index(@PathVariable("message") String message) {
return hogeService.send(message);
}
@ExceptionHandler(Exception.class)
public Map<String, Object> handleException(HttpServletRequest req, Exception e) {
return new HashMap<String, Object>() {
{
put("handleException", e.getMessage());
}
};
}
}
PonKotsuRepository.java
A sneaky Repository class. Raise PonException or KotsuException with a certain probability.
package info.maigo.lab.sample.springretry;
import java.util.Random;
import org.springframework.stereotype.Repository;
/**
*A pompous repository.
*/
@Repository
public class PonKotsuRepository {
private static final Random r = new Random(System.currentTimeMillis());
/**
*Hopefully it returns a message, but almost fails.
* @param message
* @return message
* @throws PonException PonException
* @throws KotsuException Tips Exception
*/
public String send(String message) {
int i = r.nextInt(5);
if (i < 2) {
System.out.println("PonKotsuRepository: Throws PonException.");
throw new PonException();
} else if (i < 4) {
System.out.println("PonKotsuRepository: Throws KotsuException.");
throw new KotsuException();
} else {
System.out.println("PonKotsuRepository: Returns a message.");
return "1/Message that 3 does not convey: " + message;
}
}
}
HogeService.java
A Service class that uses the sneaky Repository class. The @Retryable annotation is used to specify the number of retries and the waiting time until retry. The method with @Recover annotation is called when an exception occurs even after trying the specified number of times. This method takes the exception that occurred in the last attempt as the first argument, and takes the argument of the method with @Retryable annotation after the second argument. This time, we have prepared two methods with @Recover annotation. One is used when PonException occurs and returns the result as a string. The other is used when a KotsuException occurs and throws a RuntimeException. Annotation information used in Spring Retry is Spring \ -Retry -Simple and essential code-free retry processing framework and [org \ .springframework \ .retry \ .annotation \ (Spring Retry 1 \ .2 \ .4 \ .RELEASE API )](https://static.javadoc.io/org.springframework.retry/spring-retry/1.2. 4.RELEASE/index.html?org/springframework/retry/annotation/package-summary.html) will be helpful.
package info.maigo.lab.sample.springretry;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.retry.annotation.Recover;
@Service
public class HogeService {
@Autowired
private PonKotsuRepository repository;
@Retryable(value = {PonException.class, KotsuException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public Map<String, Object> send(String message) {
String result = repository.send(message);
return new HashMap<String, Object>() {
{
put("result", result);
}
};
}
@Recover
public Map<String, Object> recoverSend(PonException e, String message) {
System.out.println("recoverSend: PonException");
return new HashMap<String, Object>() {
{
put("error", "The last thing that happened was PonException");
}
};
}
@Recover
public Map<String, Object> recoverSend(KotsuException e, String message) {
System.out.println("recoverSend: KotsuException");
throw new RuntimeException("The last thing that happened was KotsuException");
}
}
PonException.java
Pong exception. It just inherits RuntimeException.
package info.maigo.lab.sample.springretry;
public class PonException extends RuntimeException {
}
KotsuException.java
Tips exception. It just inherits RuntimeException.
package info.maigo.lab.sample.springretry;
public class KotsuException extends RuntimeException {
}
PonKotsuRetryListener.java
A class that implements the RetryListener interface. It consists of callbacks that are called during the retry process. In this implementation, the status of retry processing is output to the standard output.
package info.maigo.lab.sample.springretry;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.listener.RetryListenerSupport;
import org.springframework.stereotype.Component;
@Component
public class PonKotsuRetryListener extends RetryListenerSupport {
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
System.out.println("PonKotsuRetryListener#close: " + getThrowableString(throwable));
super.close(context, callback, throwable);
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
System.out.println("PonKotsuRetryListener#onError: " + getThrowableString(throwable));
super.onError(context, callback, throwable);
}
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
System.out.println("PonKotsuRetryListener#open");
return super.open(context, callback);
}
private static String getThrowableString(Throwable throwable) {
return throwable == null ? "null" : throwable.getClass().getSimpleName();
}
}
JSON is output when accessing the server started by curl.
$ curl http://localhost:8080/hello
{"result":"1/Message that 3 does not convey: hello"}
View the standard output on the server side. The message can be returned normally by retrying after the Pon exception occurs.
PonKotsuRetryListener#open
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
PonKotsuRepository: Returns a message.
PonKotsuRetryListener#close: null
$ curl http://localhost:8080/foo
{"error":"The last thing that happened was PonException"}
View the standard output on the server side. It occurs in the order of Pon exception → Kotsu exception → Pon exception. For the last Pon exception, the result is returned by PonKotsuRepository # recoverSend.
PonKotsuRetryListener#open
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
PonKotsuRepository: Throws KotsuException.
PonKotsuRetryListener#onError: KotsuException
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
recoverSend: PonException
PonKotsuRetryListener#close: PonException
$ curl http://localhost:8080/bar
{"handleException":"The last thing that happened was KotsuException"}
View the standard output on the server side. It occurs in the order of Pon exception → Kotsu exception → Kotsu exception. For the last Kotsu exception, HogeController # handleException returns the result.
PonKotsuRetryListener#open
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
PonKotsuRepository: Throws KotsuException.
PonKotsuRetryListener#onError: KotsuException
PonKotsuRepository: Throws KotsuException.
PonKotsuRetryListener#onError: KotsuException
recoverSend: KotsuException
PonKotsuRetryListener#close: KotsuException
Recommended Posts