一、概述:
1.1.Spring在3.2版本增加了一个注解@ControllerAdvice
,可以与@ExceptionHandler
、@InitBinder
、@ModelAttribute
等注解注解配套使用。
简单的说,该注解可以把异常处理器应用到所有控制器,而不是单个控制器。借助该注解,我们可以实现:在独立的某个地方,比如单独一个类,定义一套对各种异常的处理机制,然后在类的签名加上注解@ControllerAdvice
,统一对 不同阶段的、不同异常 进行处理。这就是统一异常处理的原理。
1.2.对异常按阶段进行分类,大体可以分成:进入Controller前的异常 和 Service 层异常。
统一异常处理的作用: 目标就是消灭95%以上的 try catch 代码块,并以优雅的 Assert(断言) 方式来校验业务的异常情况,只关注业务逻辑,而不用花费大量精力写冗余的 try catch 代码块。 用于捕获Controller中抛出的指定类型的异常,从而达到全局不同类型的异常区别处理的目的。
1.3.优点:
- 不用强制写try-catch,由全局异常处理器统一捕获处理。
- 自定义异常,只能用全局异常来捕获。不能直接返回给客户端,客户端是看不懂的,需要接入全局异常处理器
- JSR303规范的Validator参数校验器,参数校验不通过会抛异常,是无法使用try-catch语句直接捕获,只能使用全局异常处理器。
二、统一异常处理相关注解:
@ControllerAdvice 详解
作用范围:
@ControllerAdvice 注解可以用于类级别,用于标记一个类为全局异常处理器。
它也可以与 @RestController、@Controller 注解一起使用,用于指定全局异常处理器只处理带有 @RestController 或 @Controller 注解的控制器类中抛出的异常。
异常处理:
通过在 @ControllerAdvice 注解标记的类中定义异常处理方法,可以统一处理应用程序中的各种异常。
异常处理方法需要使用 @ExceptionHandler 注解进行标记,以指定处理的异常类型。
作用域:
@ControllerAdvice 注解可以限定作用的范围,即指定哪些控制器类的异常会被当前的全局异常处理器处理。
可以通过 annotations、basePackages、basePackageClasses 等属性来指定作用的范围。
其他用途:
除了处理异常之外,@ControllerAdvice 还可以用于其他方面,比如全局数据绑定、全局数据预处理等。
5.@ExceptionHandler 详解
用法:
@ExceptionHandler 注解可以用于方法级别,用于标记一个方法为异常处理方法。
异常处理方法需要定义在控制器类中,并且可以有任意的访问修饰符。
参数:
异常处理方法的参数可以是异常类型,也可以是其他类型的参数。
如果异常处理方法的参数是异常类型,则该方法只会处理指定类型的异常。
如果异常处理方法的参数是其他类型的参数,则该方法会处理所有类型的异常,并且异常对象会作为参数传递给方法。
异常处理逻辑:
异常处理方法可以编写任意的异常处理逻辑,比如记录日志、返回错误信息、执行特定的补救措施等。
在方法中可以通过异常对象来获取异常信息,如异常消息、堆栈轨迹等。
多个异常处理方法:
一个控制器类可以有多个异常处理方法,用来处理不同类型的异常。
当多个异常处理方法都能处理同一类型的异常时,Spring 框架会选择最匹配的异常处理方法来处理异常。
总的来说,@ExceptionHandler 注解提供了一种在控制器中处理异常的机制,能够根据不同类型的异常来执行不同的异常处理逻辑,使代码更加清晰和易于维护。
三、代码实现:
3.1.pom.xml:
<?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>
<groupId>com.example</groupId>
<artifactId>08_globalexception</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>08_globalException</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<!--<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<!--添加Knife4j依赖-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
<!--引入Lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<!-- Spring Boot支持Spring Validation的依赖项,用于检查参数的基本有效性 -->
<!--有时候idea要求必须写version否则报错-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
<version>${spring-boot.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.zyq.globalException.Application</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.2.application.properties
# 应用服务 WEB 访问端口
spring.profiles.active=dev
# 使用yml配置方式,Jackson全局过滤null值
spring.jackson.default-property-inclusion=NON_NULL
3.2.application-dev.properties
server.port=9080
# 日志级别
logging.level.com.zyq.globalException=trace
# 热部署
spring.devtools.restart.enabled=true
spring.devtools.restart.additional-paths=src/main/java
3.3.统一异常处理类:
package com.zyq.globalException.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
//@RestControllerAdvice是什么
//@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成,而@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
@ResponseBody
public String handleServiceException(RuntimeException e){
log.debug("GlobalExceptionHandler---->handleServiceException()::捕获到RuntimeException:{}", e.getMessage());
return e.getMessage();
}
@ExceptionHandler
@ResponseBody
public String handleThrowable(Throwable e){
log.debug("GlobalExceptionHandler---->handleServiceException()::捕获到Throwable:{}", e.getMessage());
e.printStackTrace(); // 强烈建议
String message = "程序运行时出现未知错误,请联系系统管理员!";
return message;
}
}
//@RestControllerAdvice的特点:
//1.通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。
//2.注解了@RestControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。
//3.@RestControllerAdvice注解, 会生效于标注了 @RequestMapping的控制器的方法上。
//4.@ExceptionHandler:用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。
//5.@InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
//6.@ModelAttribute:当与@ControllerAdvice配合使用时,可以让全局的@RequestMapping都能获得在此处设置的键值对(本来作用是绑定键值对到Model中)
//注: @RestControllerAdvice和@ControllerAdvice可以用basePackages指定 Controller 范围
//basePackages: 指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理,比如:
//@RestControllerAdvice(basePackages={"com.zyq.controller"})
//@Slf4j
//public class ExceptionHandlerAdvice { }
//basePackageClasses: 是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的
// 包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理
//assignableTypes: 指定一个或多个 Controller 类,这些类被该 @ControllerAdvice 管理
//annotations: 指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理
//相关链接: https://blog.csdn.net/user2025/article/details/105458842
3.4.UserController:
package com.zyq.globalException.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class UserController {
@GetMapping("/user")
public String getUser() {
log.info("UserController.getUser");
System.out.println(5/0);
return "user";
}
@GetMapping("/user2")
public String getUser2() {
log.info("UserController.getUser2");
return "zyq-user";
}
}
3.5.主启动类:
package com.zyq.globalException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}