[JAVA] Verwenden Sie im Spring Boot @ControllerAdvice, @ExceptionHandler, HandlerExceptionResolver, um Ausnahmen abzufangen

Überblick

--Fangen Sie Ausnahmen ab, die in der Controller-Klasse in der Spring Boot-Anwendung auftreten -Fangen Sie in der Klasse mit @ControllerAdvice jede Ausnahmeklasse mit der Methode mit @ExceptionHandler ab

Diese Operation prüft die Umgebung

Quellcodeliste

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

MyApplication.java

Spring Boot Boot Klasse.

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

Eine einfache Ausnahmeklasse, die für diese Operationsprüfung vorbereitet wurde.

package com.example.my;

public class MyException extends Exception {
}

MyController.java

Eine Controller-Klasse, die das Routing übernimmt. Lösen Sie eine MyException-Ausnahme aus, wenn Sie auf http: // localhost: 8080 / myexception zugreifen. Lösen Sie eine Ausnahmebedingung aus, wenn der Zugriff auf http: // localhost: 8080 / exception erfolgt.

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 Eine Klasse zum Abfangen von Ausnahmen. Kommentieren Sie die Klasse mit @ControllerAdvice. Die Annotation @ExceptionHandler wird der Methode zum Abfangen der Ausnahme hinzugefügt, und MyException.class wird angegeben. Referenz: [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

Eine Klasse zum Abfangen von Ausnahmen, die @ExceptionHandler nicht verarbeitet. Implementiert die HandlerExceptionResolver-Schnittstelle. Fügen Sie der Klasse die Annotation @Component hinzu, um sie im DI-Container als Bean zu registrieren. Referenz: [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

Thymeleaf-Vorlagendatei für die HTML-Ausgabe. Informationen wie Fehler anzeigen.

<!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

Konfigurationsdatei zum Erstellen mit 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>

Erstellen einer JAR-Datei und Starten von Spring Boot

Generieren Sie eine JAR-Datei mit dem Befehl mvn package von Maven.

$ mvn package

Geben Sie die generierte JAR-Datei an und starten Sie den Webserver durch Spring Boot mit dem Befehl java.

$ java -jar target/my-0.0.1.jar

Greifen Sie mit Curl zu und überprüfen Sie das Verhalten

http://localhost:8080/myexception

Zugang mit Locken. Sie können sehen, dass es von der handleMyException-Methode der MyControllerAdvice-Klasse behandelt wird.

$ 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>

Spring Boot Server-seitige Ausgabe.

MyControllerAdvice#handleMyException

Besuchen Sie http: // localhost: 8080 / exception, um das Verhalten zu sehen

Zugang mit Locken. Sie können sehen, dass es von der resolveException-Methode der MyHandlerExceptionResolver-Klasse behandelt wird.

$ 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>

Spring Boot Server-seitige Ausgabe.

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

Ablauf der Ausnahmebehandlung in Spring Web MVC

Die DispatcherServlet-Klasse verarbeitet mehrere HandlerExceptionResolver-Objekte.

ExceptionHandlerExceptionResolver-Klasse, ResponseStatusExceptionResolver-Klasse, DefaultHandlerExceptionResolver-Klasse werden vorbereitet, und diese Klassen behandeln Ausnahmen mit ihren jeweiligen Rollen.

Unter diesen wird die ExceptionHandlerExceptionResolver-Klasse verarbeitet, um die Methode mit der Annotation @ExceptionHandler aufzurufen.

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).

Zeigen Sie den Quellcode für die Spring Web MVC DispatcherServlet-Klasse an.

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;
      }
    }
  }

In der processHandlerException-Methode wird das HandlerExceptionResolver-Objekt einzeln von this.handlerExceptionResolvers erfasst und die resolveException-Methode aufgerufen. Die resolveException-Methode jedes Objekts behandelt die Ausnahme und gibt ein ModelAndView-Objekt zurück.

Schauen wir uns das Objekt an, das vom IntelliJ IDEA-Debugger verarbeitet wird.

dispatcherservlet-1.png dispatcherservlet-2.png

Sie können sehen, dass das HandlerExceptionResolverComposite-Objekt den ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver und DefaultHandlerExceptionResolver verwaltet.

Das ExceptionHandlerExceptionResolver-Objekt verfügt über eine Instanzvariable namens exceptionHandlerAdviceCache, die ein Objekt der mit @ControllerAdvice kommentierten Klasse enthält.

Wenn Sie sich den Quellcode von ExceptionHandlerExceptionResolver ansehen, können Sie sehen, dass er in exceptionHandlerAdviceCache registriert ist.

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()));
}

Referenzmaterial

Recommended Posts

Verwenden Sie im Spring Boot @ControllerAdvice, @ExceptionHandler, HandlerExceptionResolver, um Ausnahmen abzufangen
Verwendung von CommandLineRunner im Spring Batch von Spring Boot
Wie man Lombok im Frühling benutzt
Aufrufen und Verwenden der API in Java (Spring Boot)
Verwendung von ModelMapper (Spring Boot)
Verwenden Sie die DynamoDB-Abfragemethode mit Spring Boot
Verwendung von MyBatis2 (iBatis) mit Spring Boot 1.4 (Spring 4)
Verwendung des eingebauten h2db mit Federstiefel
Verwendung von Spring Boot-Sitzungsattributen (@SessionAttributes)
Servlet-Filter mit Spring Boot verwenden [Spring Boot 1.x, 2.x kompatibel]
So fügen Sie in Spring Boot einen Klassenpfad hinzu
So binden Sie mit einer Eigenschaftendatei in Spring Boot
Verwenden Sie Interceptor im Frühjahr
So erstellen Sie ein Spring Boot-Projekt in IntelliJ
Führen Sie swagger-ui in die in Spring Boot implementierte REST-API ein
Verwendung des In-Memory-Job-Repositorys mit Spring Batch
Legen Sie den Kontextparameter in Spring Boot fest
Versuchen Sie Spring Boot von 0 bis 100.
So ändern Sie den Einstellungswert von application.properties beim Booten im Frühjahrsstart
Spring Boot 2 Multiprojekt mit Gradle
Verwendung derselben Mapper-Klasse in mehreren Datenquellen mit Spring Boot + MyBatis
Einführung in Spring Boot ~ ~ DI ~
Einführung in Spring Boot ② ~ AOP ~
Spring Boot + Springfox Springfox-Boot-Starter 3.0.0 Verwendung
Verwenden Sie thymeleaf3 mit parent, ohne Spring-Boot-Starter-Parent in Spring Boot anzugeben
Verwenden Sie Spring JDBC mit Spring Boot
Wichtige Änderungen in Spring Boot 1.5
Einführung in Spring Boot Teil 1
So steuern Sie Transaktionen in Spring Boot ohne Verwendung von @Transactional
[Sprint Boot] Verwendung der drei in org.springframework.jdbc.core.namedparam definierten Arten von SqlParameterSource
Was ich bei der Migration von der Spring Boot 1.4-Serie zur 2.0-Serie getan habe
Was ich bei der Migration von der Spring Boot 1.5-Serie zur 2.0-Serie getan habe
Verwenden Sie die Standardauthentifizierung mit Spring Boot
Spring Boot-Anwendungsentwicklung in Eclipse
Hinweise zur Verwendung von Spring Data JDBC
Schreiben Sie den Testcode mit Spring Boot
So stellen Sie Spring Boot + PostgreSQL ein
Verwendung von InjectorHolder in OpenAM
Implementieren Sie die REST-API mit Spring Boot
Was ist @Autowired im Spring Boot?
Verwenden Sie DBUnit für den Spring Boot-Test
Rüsten Sie den Federstiefel von der 1.5-Serie auf die 2.0-Serie auf
Wie verwende ich Klassen in Java?
Beginnend mit Spring Boot 0. Verwenden Sie Spring CLI
Fangen Sie mehrere Ausnahmen zusammen in Java
Verwendung von Thymeleaf mit Spring Boot
Spring.messages.fallback-to-system-locale: false ist erforderlich, um message.properties für die i18n-Unterstützung von Spring boot als Standard festzulegen
Schritte zum Ausführen von Spring Boot beziehen sich auf die Werte in der Eigenschaftendatei
Mein Memorandum, dass ich ValidationMessages.properties UTF8 in Spring Boot erstellen möchte
[Für den internen Gebrauch] Für diejenigen, die dem Spring Boot-Projekt zugewiesen sind (im Aufbau)
Mehrsprachige Unterstützung für Java Verwendung des Gebietsschemas
[Einführung in Spring Boot] Überprüfung der Formularvalidierung
Konvertieren Sie Anforderungsparameter im Frühjahr in Enum
Erstellen Sie mit Gradle ein Spring Boot + Docker-Image
Statische Dateizugriffspriorität beim Spring Boot
Wie kann ich Spring Tool in Eclipse 4.6.3 einbinden?
Spring Boot-Protokoll im JSON-Format ausgeben
Memorandum zum Herunterladen lokaler Dateien mit Spring Boot
Verwendung von Docker in VSCode DevContainer
Erstellen Sie mit IntelliJ ein Java Spring Boot-Projekt