[JAVA] Utilisez @ControllerAdvice, @ExceptionHandler, HandlerExceptionResolver dans Spring Boot pour intercepter les exceptions

Aperçu

--Catch exceptions qui se produisent dans la classe de contrôleur dans l'application Spring Boot -Dans la classe avec @ControllerAdvice, attraper chaque classe d'exception avec la méthode avec @ExceptionHandler

Cette opération vérifie l'environnement

Liste des codes sources

├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── my
        │               ├── MyApplication.java
        │               ├── MyController.java
        │               ├── MyControllerAdvice.java
        │               ├── MyException.java
        │               └── MyHandlerExceptionResolver.java
        └── resources
            └── templates
                └── myview.html

MyApplication.java

Classe de démarrage Spring Boot.

package com.example.my;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

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

MyException.java

Une classe d'exception simple préparée pour ce contrôle d'opération.

package com.example.my;

public class MyException extends Exception {
}

MyController.java

Une classe de contrôleur qui gère le routage. Déclenchez une exception MyException lors de l'accès à http: // localhost: 8080 / myexception. Soulevez une exception d'exception lorsque l'accès à http: // localhost: 8080 / exception arrive.

package com.example.my;

import org.springframework.boot.SpringApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {

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

  @RequestMapping("/")
  public ModelAndView handleTop(ModelAndView mav) {
    mav.addObject("mymessage", "Hello, world.");
    mav.setViewName("myview");
    return mav;
  }

  @RequestMapping("/myexception")
  public ModelAndView handleMyException(ModelAndView mav) throws MyException {
    throw new MyException();
  }

  @RequestMapping("/exception")
  public ModelAndView handleException(ModelAndView mav) throws Exception {
    throw new Exception();
  }
}

MyControllerAdvice.java

MyException Une classe pour intercepter les exceptions. Annotez la classe avec @ControllerAdvice. L'annotation @ExceptionHandler est ajoutée à la méthode pour intercepter l'exception et MyException.class est spécifié. Référence: [ExceptionHandler \ (Spring Framework 5 \ .1 \ .9 \ .RELEASE API )](https://docs.spring.io/spring/docs/5.1.9.RELEASE/javadoc-api/org/springframework /web/bind/annotation/ExceptionHandler.html)

package com.example.my;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class MyControllerAdvice {

  @ExceptionHandler({MyException.class})
  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  public ModelAndView handleMyException(Exception e, WebRequest req) {
    System.out.println("MyControllerAdvice#handleMyException");
    ModelAndView mav = new ModelAndView();
    mav.addObject("myerror", "MyControllerAdvice#handleMyException");
    mav.setViewName("myview");
    return mav;
  }
}

MyHandlerExceptionResolver.java

Une classe pour intercepter les exceptions que @ExceptionHandler ne gère pas. Implémente l'interface HandlerExceptionResolver. Ajoutez l'annotation @Component à la classe pour l'enregistrer dans le conteneur DI en tant que bean. Référence: [HandlerExceptionResolver \ (Spring Framework 5 \ .1 \ .9 \ .RELEASE API )](https://docs.spring.io/spring/docs/5.1.9.RELEASE/javadoc-api/org/springframework /web/servlet/HandlerExceptionResolver.html)

package com.example.my;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {

  @Override
  public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    System.out.println("MyHandlerExceptionResolver#resolveException");
    System.out.println(handler.getClass());
    System.out.println(handler);
    ModelAndView mav = new ModelAndView();
    mav.addObject("myerror", "MyHandlerExceptionResolver#resolveException");
    mav.setViewName("myview");
    mav.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
    return mav;
  }
}

myview.html

Fichier modèle Thymeleaf pour la sortie HTML. Affichez des informations telles que des erreurs.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div th:if="${myerror}">
    <div>Error: <span th:text="${myerror}"></span></div>
</div>
<div th:if="${mymessage}">
    <div>Message: <span th:text="${mymessage}"></span></div>
</div>
</body>
</html>

pom.xml

Fichier de configuration pour la construction avec Maven.

<?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 https://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.1.7.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <groupId>com.example</groupId>
  <artifactId>my</artifactId>
  <version>0.0.1</version>
  <name>my</name>
  <description>My project for Spring Boot</description>

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

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

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

</project>

Créer un fichier JAR et lancer Spring Boot

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

$ mvn package

Spécifiez le fichier JAR généré et démarrez le serveur Web par Spring Boot avec la commande java.

$ java -jar target/my-0.0.1.jar

Accédez avec curl et vérifiez le comportement

http://localhost:8080/myexception

Accès avec curl. Vous pouvez voir qu'il est géré par la méthode handleMyException de la classe MyControllerAdvice.

$ curl http://localhost:8080/myexception
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <div>Error: <span>MyControllerAdvice#handleMyException</span></div>
</div>

</body>
</html>

Sortie côté Spring Boot Server.

MyControllerAdvice#handleMyException

Visitez http: // localhost: 8080 / exception pour voir le comportement

Accès avec curl. Vous pouvez voir qu'il est géré par la méthode resolverException de la classe MyHandlerExceptionResolver.

$ curl http://localhost:8080/exception
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <div>Error: <span>MyHandlerExceptionResolver#resolveException</span></div>
</div>

</body>
</html>

Sortie côté Spring Boot Server.

MyHandlerExceptionResolver#resolveException
class org.springframework.web.method.HandlerMethod
public org.springframework.web.servlet.ModelAndView com.example.my.MyController.handleException(org.springframework.web.servlet.ModelAndView) throws java.lang.Exception

Flux de gestion des exceptions dans Spring Web MVC

La classe DispatcherServlet gère plusieurs objets HandlerExceptionResolver.

La classe ExceptionHandlerExceptionResolver, la classe ResponseStatusExceptionResolver, la classe DefaultHandlerExceptionResolver sont préparées et ces classes gèrent les exceptions avec leurs rôles respectifs.

Parmi eux, la classe ExceptionHandlerExceptionResolver est en train d'appeler la méthode avec l'annotation @ExceptionHandler.

DispatcherServlet (Spring Framework 5.1.9.RELEASE API)

The dispatcher's exception resolution strategy can be specified via a HandlerExceptionResolver, for example mapping certain exceptions to error pages. Default are ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver, and DefaultHandlerExceptionResolver. These HandlerExceptionResolvers can be overridden through the application context. HandlerExceptionResolver can be given any bean name (they are tested by type).

Affichez le code source de la classe Spring Web MVC DispatcherServlet.

spring-framework/DispatcherServlet.java at v5.1.9.RELEASE · spring-projects/spring-framework

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
    @Nullable Object handler, Exception ex) throws Exception {

  // Success and error responses may use different content types
  request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

  // Check registered HandlerExceptionResolvers...
  ModelAndView exMv = null;
  if (this.handlerExceptionResolvers != null) {
    for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
      exMv = resolver.resolveException(request, response, handler, ex);
      if (exMv != null) {
        break;
      }
    }
  }

Dans la méthode processHandlerException, l'objet HandlerExceptionResolver est acquis un par un à partir de this.handlerExceptionResolvers et la méthode resolException est appelée. La méthode resolException de chaque objet gère l'exception et renvoie un objet ModelAndView.

Jetons un coup d'œil à l'objet en cours de traitement par le débogueur IntelliJ IDEA.

dispatcherservlet-1.png dispatcherservlet-2.png

Vous pouvez voir que l'objet HandlerExceptionResolverComposite gère les ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver et DefaultHandlerExceptionResolver.

L'objet ExceptionHandlerExceptionResolver a une variable d'instance appelée exceptionHandlerAdviceCache, qui contient un objet de la classe annoté avec @ControllerAdvice.

Si vous regardez le code source de ExceptionHandlerExceptionResolver, vous pouvez voir qu'il est inscrit dans exceptionHandlerAdviceCache.

spring-framework/ExceptionHandlerExceptionResolver.java at v5.1.9.RELEASE · spring-projects/spring-framework

private void initExceptionHandlerAdviceCache() {
  if (getApplicationContext() == null) {
    return;
  }

  List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
  AnnotationAwareOrderComparator.sort(adviceBeans);

  for (ControllerAdviceBean adviceBean : adviceBeans) {
    Class<?> beanType = adviceBean.getBeanType();
    if (beanType == null) {
      throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
    }
    ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
    if (resolver.hasExceptionMappings()) {
      this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
    }
    if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
      this.responseBodyAdvice.add(adviceBean);
    }
  }

spring-framework/ExceptionHandlerMethodResolver.java at v5.1.9.RELEASE · spring-projects/spring-framework

private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
  ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
  Assert.state(ann != null, "No ExceptionHandler annotation");
  result.addAll(Arrays.asList(ann.value()));
}

Matériel de référence

Recommended Posts

Utilisez @ControllerAdvice, @ExceptionHandler, HandlerExceptionResolver dans Spring Boot pour intercepter les exceptions
Comment utiliser CommandLineRunner dans Spring Batch of Spring Boot
Comment utiliser Lombok au printemps
Comment appeler et utiliser l'API en Java (Spring Boot)
Comment utiliser ModelMapper (Spring boot)
Utiliser la méthode de requête DynamoDB avec Spring Boot
Comment utiliser MyBatis2 (iBatis) avec Spring Boot 1.4 (Spring 4)
Comment utiliser h2db intégré avec Spring Boot
Comment utiliser les attributs de session Spring Boot (@SessionAttributes)
Utiliser le filtre de servlet avec Spring Boot [compatible Spring Boot 1.x, 2.x]
Comment ajouter un chemin de classe dans Spring Boot
Comment se lier avec un fichier de propriétés dans Spring Boot
Utilisez Interceptor au printemps
Comment créer un projet Spring Boot dans IntelliJ
Présentez swagger-ui à l'API REST implémentée dans Spring Boot
Comment utiliser le référentiel de jobs en mémoire avec Spring Batch
Définir le paramètre contextuel dans Spring Boot
Essayez Spring Boot de 0 à 100.
Comment modifier la valeur de paramètre de application.properties au moment du démarrage dans Spring Boot
Multi-projets Spring Boot 2 avec Gradle
Comment utiliser la même classe Mapper dans plusieurs sources de données avec Spring Boot + MyBatis
Introduction à Spring Boot ① ~ DI ~
Introduction à Spring Boot ② ~ AOP ~
Spring Boot + Springfox springfox-boot-starter 3.0.0 Utilisation
Utilisez thymeleaf3 avec le parent sans spécifier spring-boot-starter-parent dans Spring Boot
Utiliser Spring JDBC avec Spring Boot
Changements majeurs dans Spring Boot 1.5
Introduction à Spring Boot, partie 1
Comment contrôler les transactions dans Spring Boot sans utiliser @Transactional
[Sprint Boot] Comment utiliser les trois types de SqlParameterSource définis dans org.springframework.jdbc.core.namedparam
Ce que j'ai fait lors de la migration de la série Spring Boot 1.4 vers la série 2.0
Ce que j'ai fait lors de la migration de la série Spring Boot 1.5 vers la série 2.0
Utiliser l'authentification de base avec Spring Boot
Développement d'applications Spring Boot dans Eclipse
Remarques sur l'utilisation de Spring Data JDBC
Écrire du code de test avec Spring Boot
Comment configurer Spring Boot + PostgreSQL
Comment utiliser InjectorHolder dans OpenAM
Implémenter l'API REST avec Spring Boot
Qu'est-ce que @Autowired dans Spring Boot?
Utiliser DBUnit pour le test Spring Boot
Mise à niveau de la botte à ressort de la série 1.5 à la série 2.0
Comment utiliser les classes en Java?
À partir de Spring Boot 0. Utilisez Spring CLI
Attrapez plusieurs exceptions ensemble dans Java
Comment utiliser Thymeleaf avec Spring Boot
Spring.messages.fallback-to-system-locale: false est requis pour le message par défaut.properties pour la prise en charge i18n de Spring Boot
Étapes pour rendre Spring Boot capable de faire référence à la valeur dans le fichier de propriétés
Mon mémorandum que je veux faire ValidationMessages.properties UTF8 dans Spring Boot
[Pour usage interne] Pour ceux affectés au projet Spring Boot (en construction)
Prise en charge multilingue de Java Comment utiliser les paramètres régionaux
[Introduction à Spring Boot] Vérification de la validation du formulaire
Convertir les paramètres de demande en Enum au printemps
Créer une image Spring Boot + Docker avec Gradle
Priorité d'accès aux fichiers statiques dans Spring Boot
Comment inclure Spring Tool dans Eclipse 4.6.3?
Sortie du journal Spring Boot au format json
Mémorandum de téléchargement de fichier local avec Spring Boot
Comment utiliser Docker dans VSCode DevContainer
Créer un projet Java Spring Boot avec IntelliJ