[JAVA] Vérifiez le comportement de include, exclude et ExhaustedRetryException de Spring Retry

Aperçu

Liste du code source de l'exemple de programme

├── pom.xml
└── src
    └── main
        └── java
            └── info
                └── maigo
                    └── lab
                        └── sample
                            └── retry
                                └── exhausted
                                    ├── HogeApplication.java
                                    ├── HogeController.java
                                    ├── HogeException.java
                                    ├── HogeHogeException.java
                                    └── HogeService.java

Environnement de contrôle de fonctionnement

Fichier de construction Maven pom.xml

Cette fois, exécutez la compilation avec Maven. pom.xml est basé sur celui généré par Spring Initializr. Ajoutez spring-retry aux dépendances pour utiliser Spring Retry. Ajoutez également spring-boot-starter-aop car la classe AOP est requise lors de l'exécution.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.M4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>info.maigo.lab</groupId>
    <artifactId>sample.retry.exhausted</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sample.retry.exhausted</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
            <version>1.2.4.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </pluginRepository>
    </pluginRepositories>

</project>

Construire et démarrer le serveur

Générez un fichier JAR avec la commande mvn package.

$ mvn package

Démarrez le serveur en spécifiant le fichier JAR avec la commande java.

$ java -jar target/sample.retry.exhausted-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.2.0.M4)

Code source

HogeApplication.java

Classe d'entrée de démarrage par Spring Boot. L'annotation @EnableRetry est spécifiée pour utiliser Spring Retry.

package info.maigo.lab.sample.retry.exhausted;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;

@SpringBootApplication
@EnableRetry
public class HogeApplication {

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

}

HogeController.java

Classe de contrôleur qui reçoit une requête HTTP et renvoie une réponse. Lorsqu'une exception se produit, gérez-la avec la méthode qui spécifie l'annotation @ExceptionHandler.

package info.maigo.lab.sample.retry.exhausted;

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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HogeController {

    @Autowired
    private HogeService hogeService;

    @RequestMapping("/")
    public Map<String, Object> index(@RequestParam(name = "name", defaultValue = "java.lang.RuntimeException") String name) throws Exception {
        return new HashMap<String, Object>() {
            {
                put("result", hogeService.invoke(name));
            }
        };
    }

    @ExceptionHandler(Exception.class)
    public Map<String, Object> handleException(HttpServletRequest req, Exception e) {
        return new HashMap<String, Object>() {
            {
                put("handleException", e.getClass().getName() + " / " + e.getMessage());
            }
        };
    }
}

HogeException.java

Une classe d'exception qui hérite uniquement d'Exception.

package info.maigo.lab.sample.retry.exhausted;

public class HogeException extends Exception {
}

HogeHogeException.java

Une classe d'exception qui hérite de HogeException.

package info.maigo.lab.sample.retry.exhausted;

public class HogeHogeException extends HogeException {
}

HogeService.java

Classe de service. La méthode invoke crée un objet d'exception basé sur la chaîne spécifiée et le lève. L'annotation @Retryable est spécifiée dans la méthode invoke. Inclure spécifie le type d'exception à réessayer et exclure spécifie le type d'exception à ne pas réessayer. Spécifiez le nombre de tentatives avec maxAttempts. Spécifiez le temps d'attente avant une nouvelle tentative avec interruption. La méthode avec l'annotation @Recover est appelée lorsqu'une exception se produit même après avoir essayé le nombre de fois spécifié (même si l'exception n'est pas la cible de la nouvelle tentative, cette méthode est appelée si elle correspond au type).

package info.maigo.lab.sample.retry.exhausted;

import java.lang.reflect.Constructor;
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 {

    @Retryable(
        include = {HogeException.class},
        exclude = {HogeHogeException.class},
        maxAttempts = 3,
        backoff = @Backoff(delay = 1000))
    public String invoke(String name) throws Exception {
        System.out.println("HogeService#invoke: " + name);
        Class cls = Class.forName(name);
        Constructor cnst = cls.getDeclaredConstructor();
        Exception e = (Exception) cnst.newInstance();
        throw e;
    }

    @Recover
    public String recover(HogeException e, String name) {
        System.out.println("HogeService#recover: " + name);
        return "HogeService#recover: " + e.getClass().getName();
    }
}

Exemple d'exécution

Lancez HogeException et réessayez

Lorsque vous accédez au serveur démarré avec curl, JSON est généré. Spécifie qu'une HogeException doit se produire. La méthode recover renvoie une représentation sous forme de chaîne de HogeException.

$ curl http://localhost:8080/?name=info.maigo.lab.sample.retry.exhausted.HogeException
{"result":"HogeService#recover: info.maigo.lab.sample.retry.exhausted.HogeException"}

Affichez le journal de sortie standard côté serveur. Vous pouvez voir que la méthode de récupération est appelée après l'exécution de la nouvelle tentative et que la méthode d'invocation est appelée trois fois.

HogeService#invoke: info.maigo.lab.sample.retry.exhausted.HogeException
HogeService#invoke: info.maigo.lab.sample.retry.exhausted.HogeException
HogeService#invoke: info.maigo.lab.sample.retry.exhausted.HogeException
HogeService#recover: info.maigo.lab.sample.retry.exhausted.HogeException

Étant donné que HogeException est spécifié dans l'annotation include of @Retryable, le traitement de nouvelle tentative est exécuté. Si une exception se produit lors de la dernière tentative, la méthode avec l'annotation @Recover est appelée.

Ne pas faire réessayer HogeHogeException

Accédez avec curl en spécifiant que HogeHogeException se produira. La méthode recover renvoie une représentation sous forme de chaîne de HogeHogeException.

$ curl http://localhost:8080/?name=info.maigo.lab.sample.retry.exhausted.HogeHogeException
{"result":"HogeService#recover: info.maigo.lab.sample.retry.exhausted.HogeHogeException"}

Affichez le journal de sortie standard côté serveur. Vous pouvez voir que la méthode de récupération est appelée après que la méthode invoke a été appelée une seule fois.

HogeService#invoke: info.maigo.lab.sample.retry.exhausted.HogeHogeException
HogeService#recover: info.maigo.lab.sample.retry.exhausted.HogeHogeException

HogeHogeException est spécifié dans l'exclusion de l'annotation @Retryable, le traitement de nouvelle tentative n'est donc pas effectué. J'ai appelé la méthode invoke une fois et j'ai obtenu une HogeHogeException, et la méthode recover a été appelée et la chaîne résultante a été renvoyée. De plus, HogeHogeException est une sous-classe de HogeException spécifiée dans include of @Retryable annotation, donc si HogeHogeException n'est pas spécifié dans exclude, il sera retenté.

ExhaustedRetryException se produit lorsque @Recover lève une exception qui ne peut pas être interceptée

Accédez avec curl en spécifiant que java.lang.Exception se produira. Une exception org.springframework.retry.ExhaustedRetryException est levée sans être interceptée par la méthode de récupération.

$ curl http://localhost:8080/?name=java.lang.Exception
{"handleException":"org.springframework.retry.ExhaustedRetryException / Cannot locate recovery method; nested exception is java.lang.Exception"}

Affichez le journal de sortie standard côté serveur. Vous pouvez voir que la méthode invoke n'est appelée qu'une seule fois. La méthode de récupération n'a pas été appelée.

HogeService#invoke: java.lang.Exception

Étant donné que java.lang.Exception n'est pas spécifié dans l'annotation include of @Retryable, le traitement des nouvelles tentatives n'est pas effectué. En outre, ExhaustedRetryException est levée car l'annotation @Recover n'est pas le type d'exception qui peut être interceptée par la méthode spécifiée. Dans ce cas, la méthode getCause de la classe ExhaustedRetryException peut être utilisée pour obtenir l'objet de l'exception java.lang.Exception qui l'a provoquée.

Matériel de référence

Recommended Posts

Vérifiez le comportement de include, exclude et ExhaustedRetryException de Spring Retry
[Java] [Spring] Tester le comportement de l'enregistreur
Vérifions la sensation de Spring Boot + Swagger 2.0
Vérifiez le comportement de Java Intrinsic Locks avec bpftrace
Vérifiez le comportement de getOne, findById et des méthodes de requête avec Spring Boot + Spring Data JPA
À propos du comportement de ruby Hash # ==
[Rails] Vérifiez le contenu de l'objet
Vérifiez la version de Cent OS
Partie 4: Personnalisez le comportement de la connexion OAuth 2.0 prise en charge par Spring Security 5
Vérifier l'état de migration des rails
Voir le comportement des mises à jour d'entités avec Spring Boot + Spring Data JPA
Filtrer le résultat de BindingResult [Spring]
L'histoire de la rencontre avec l'annotation personnalisée Spring
Vérifiez le contenu des paramètres avec le levier
Mémo: [Java] Vérifiez le contenu du répertoire
À propos de l'affichage initial de Spring Framework
Vérifiez la version du logiciel Web standard.
[Java] Vérifiez le nombre d'occurrences de caractères
Examiner le comportement du délai d'expiration des transactions JPA
Vérifiez le fonctionnement de l'interface à travers le thread
Organisez les différences de comportement de @NotBlank, @NotEmpty et @NotNull avec Spring Boot + Thymeleaf
Vérifiez la version de JDK installée et la version de JDK activée
À propos du guide de démarrage officiel de Spring Framework
L'histoire de la montée de la série Spring Boot 1.5 à la série 2.1
Le nom officiel de Spring MVC est Spring Web MVC