之前在 SpringBoot 参数校验 中已经简单介绍了异常的处理,在这篇文章中再系统总结一下。
在一个类上面加 @ControllerAdvice
或者 @RestControllerAdvice
注解,就是定义该类为异常处理类。如果不带任何参数,那么这个类就是全局异常处理类,例如在 SpringBoot 参数校验 中我们创建的 GlobalExceptionHandler
。有时候如果不希望捕获全局异常,可以通过给注解传递参数,指定处理的 Controller 范围:
1. basePackages
:指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice
管理
@ControllerAdvice(basePackages = {
"com.tfjybj.intern.provider.controller"})
public class FaultBarrier{
}
2. basePackageClasses
:是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice
管理
@ControllerAdvice(basePackageClasses = {
Test.class})
public class FaultBarrier{
}
3. assignableTypes
:指定一个或多个 Controller 类,这些类被该 @ControllerAdvice
管理
@ControllerAdvice(assignableTypes= {
Test.class})
public class FaultBarrier{
}
4. annotations
:指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice
管理
@ControllerAdvice(annotations = {
RestController.class})
public class FaultBarrier{
}
另外还要注意下,{}
在 Java 中表示数组,例如我们想指定多个注解,可以这样用:
@ControllerAdvice(annotations = {
RestController1.class, RestController2.class})
public class FaultBarrier{
}
在异常处理类中,我们可以使用 @ExceptionHandler
注解定义异常处理方法,其中 value
为需要处理的异常类的 class :
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity<ServerResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return ServerResponse.badRequest(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
}
}
这边需要注意下, @ControllerAdvice
或者 @RestControllerAdvice
只能处理 Controller 层抛出的异常,对例如 Interceptor(拦截器)层的异常、定时任务中的异常、异步方法中的异常,不会进行处理。此外,404 的错误也是无法捕获的:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = NoHandlerFoundException.class)
public ResponseEntity<ServerResponse> handleNoHandlerFoundException(NoHandlerFoundException e) {
// @RestControllerAdvice 只能捕获 Controller 抛出的异常
// NoHandlerFoundException 实际上是捕获不到的
log.error("======not found 异常======");
log.error(e.getMessage());
return ServerResponse.notFound();
}
}
对于上面这种不是通过 Controller 抛出的异常,使用 @ControllerAdvice
和 @RestControllerAdvice
异常处理类无法捕获到,那么如何处理 404 异常呢?本人找到了一种方案,创建一个类并实现 ErrorController
接口,就可以处理非 Controller 抛出的异常:
@RestController
@Slf4j
public class ServerErrorController implements ErrorController {
@Override
public String getErrorPath() {
// 实现 ErrorController 接口必须要实现 getErrorPath 方法
// getErrorPath 方法返回的 path,会映射到 handleError 方法
return "/error";
}
@RequestMapping(value = "/error", produces = {
MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<ServerResponse> handleError(HttpServletResponse response) {
if (response.getStatus() == 404) {
return ServerResponse.notFound();
} else if (response.getStatus() == 500){
return ServerResponse.internalServerError();
} else {
return ServerResponse.ok(null);
}
}
}
在 SpringBoot 中的默认异常处理映射为 “/error”,BasicErrorController
已经默认实现了 “text/html”
的处理。如果想返回自定义 JSON 格式信息,则实现 ErrorController
接口,重写 getErrorPath
方法。这个方法返回一个 String ,也就是异常处理映射,然后增加一个 produces 为 “application/json”
的方法即可。
SpringBoot中实现拦截器, 并实现对404和500等错误的拦截
Spring Boot 实现ErrorController接口处理404、500等错误页面