一、概述:
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); } }