SpringBoot全局异常处理(最佳实践)

您所在的位置:网站首页 getoutputstream异常处理 SpringBoot全局异常处理(最佳实践)

SpringBoot全局异常处理(最佳实践)

2024-07-14 08:27| 来源: 网络整理| 查看: 265

目录

异常处理方案分类

基于请求转发

基于异常处理器

基于过滤器

常见异常处理实现方案

BasicExceptionController

@ExceptionHandler

@ControllerAdvice+@ExceptionHandler

SimpleMappingExceptionResolver

HandlerExceptionResolver

Filter

全局异常处理实现方案

请求转发

异常处理器+请求转发补充

过滤器

异常处理器+过滤器补充

注意事项

方案推荐

异常处理方案分类

异常处理主要分为三类:

基于请求转发的方式处理异常;

基于异常处理器的方式处理异常;

基于过滤器的方式处理异常。

基于请求转发

基于请求转发的异常处理方式是真正的全局异常处理。

实现方式有:

BasicExceptionController

基于异常处理器

基于异常处理器的异常处理方式其实并不是真正的全局异常处理,因为它处理不了过滤器等抛出的异常。

实现方式有:

@ExceptionHandler

@ControllerAdvice+@ExceptionHandler

SimpleMappingExceptionResolver

HandlerExceptionResolver

基于过滤器

基于过滤器的异常处理方式近似与全局异常处理。它能处理过滤器及之后的环节抛出的异常。

实现方式有:

Filter

常见异常处理实现方案 1. BasicExceptionController

这是SpringBoot默认处理异常方式:一旦程序中出现了异常SpringBoot就会请求/error的url,在SpringBoot中提供了一个叫BasicExceptionController的类来处理/error请求,然后跳转到默认显示异常的页面来展示异常信息。显示异常的页面也可以自定义,在目录src/main/resources/templates/下定义一个叫error的文件,可以是jsp也可以是html 。

此种方式是通过请求转发实现的,出现异常时,会转发到请求到/error,该接口对异常进行处理返回。是最符合全局异常处理的。

可以自定义Controller继承BasicErrorController异常处理来实现异常处理的自定义。

@Slf4j @RestController public class MyErrorController extends BasicErrorController {     public MyErrorController() {         super(new DefaultErrorAttributes(), new ErrorProperties());     }     /**      * produces 设置返回的数据类型:application/json      * @param request 请求      * @return 自定义的返回实体类      */     @Override     @RequestMapping(value = "", produces = {MediaType.APPLICATION_JSON_VALUE})     public ResponseEntity error(HttpServletRequest request) {         // 获取错误信息         Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));         HttpStatus status = getStatus(request);         String code = body.get("status").toString();         String message = body.get("message").toString();         return new ResponseEntity(ApiUtil.fail(message), HttpStatus.OK);     } }

需要注意

1.该种方式能获取到的信息时有限的。一般情况只能获取到下面这几个参数(特殊情况会有补充参数)。

076d7da3b6b966212dd83bcf55e6b07d.png

现在一般项目需要的响应信息都是自定义统一格式的JSON(code、msg、data)。对于自定义业务错误码code不好得到,对于错误信息msg有时得到的也不一定是你所想要的(简单说就是一些特殊的异常描述信息不好得到)。

比如:自定义的参数校验信息

@NotNull(message = "主键不能为空") 5e81f2246b4f94724bf5adb8f41c6f32.png

message参数取到的并不是“主键不能为空”。

2.当出现抛出两次异常,第一次被异常处理器处理,第二次异常转由BasicExceptionController处理。但能取到的异常信息可能是一次的,具体原因下面有分析。

2. @ExceptionHandler

该种方式只能作用于使用@ExceptionHandler注解的Controller的异常,对于其他Controller的异常就无能为力了,所以并不不推荐使用。

此种方式是通过异常处理器实现的,使用HandlerExceptionResolverComposite异常处理器中的ExceptionHandlerExceptionResolver异常处理器处理的。

@RestController public class TestController {     @GetMapping("test9")     public FundInfo test9() throws Exception {         throw new Exception("test9 error");     }     @GetMapping("test10")     public FundInfo test10() throws Exception {         throw new IOException("test10 error");     }     @ExceptionHandler(Exception.class)     public ApiResult exceptionHandler(Exception e) {         return ApiUtil.custom(500, e.getMessage());     } } ❝

注意:如果既在具体Controller使用了@ExceptionHandler,也定义了全局异常处理器类(@ControllerAdvice+@ExceptionHandler),优先使用Controller定义的@ExceptionHandler处理。如果处理不了,才会使用全局异常处理器处理。

❞ 3. @ControllerAdvice+@ExceptionHandler

使用 @ControllerAdvice+@ExceptionHandler注解能够进行近似全局异常处理,这种方式推荐使用。

一般说它只能处理控制器中抛出的异常,这种说法并不准确,其实它能处理DispatcherServlet.doDispatch方法中DispatcherServlet.processDispatchResult方法之前捕捉到的所有异常,包括:拦截器、参数绑定(参数解析、参数转换、参数校验)、控制器、返回值处理等模块抛出的异常。

此种方式是通过异常处理器实现的,使用HandlerExceptionResolverComposite异常处理器中的ExceptionHandlerExceptionResolver异常处理器处理的。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  ......省略代码......  try {   ModelAndView mv = null;   Exception dispatchException = null;   try {    ......省略代码......    mappedHandler = getHandler(processedRequest);    ......省略代码......    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());    ......省略代码......    if (!mappedHandler.applyPreHandle(processedRequest, response)) {     return;    }    ......省略代码......    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());    if (asyncManager.isConcurrentHandlingStarted()) {     return;    }    ......省略代码......    mappedHandler.applyPostHandle(processedRequest, response, mv);   }   catch (Exception ex) {    dispatchException = ex;   }   catch (Throwable err) {    dispatchException = new NestedServletException("Handler dispatch failed", err);   }   processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  }  catch (Exception ex) {   ......省略代码......  }  catch (Throwable err) {   ......省略代码......  }  finally {   ......省略代码......  } }

使用方式

定义一个类,使用@ControllerAdvice注解该类,使用@ExceptionHandler注解方法。@RestControllerAdvice注解是@ControllerAdvice注解的扩展(@RestControllerAdvice=@ControllerAdvice+@ResponseBody),返回值自动为JSON的形式。

/**  * 全局异常处理器  */ @Slf4j @SuppressWarnings("ALL") @RestControllerAdvice public class MyGlobalExceptionHandler {     @ExceptionHandler(BindException.class)     @ResponseStatus(HttpStatus.OK)     public ApiResult bindException(HttpServletRequest request,                                    HttpServletResponse response,                                    BindException exception) {         return ApiUtil.fail(exception.getBindingResult().getFieldError().getDefaultMessage());     }     @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class)     @ResponseStatus(HttpStatus.OK)     public ApiResult methodArgumentNotValidException(HttpServletRequest request,                                                      HttpServletResponse response,                                                      MethodArgumentNotValidException exception) {         return ApiUtil.fail(exception.getBindingResult().getFieldError().getDefaultMessage());     }     @ExceptionHandler(MissingServletRequestParameterException.class)     @ResponseStatus(HttpStatus.OK)     public ApiResult methodArgumentNotValidException(HttpServletRequest request,                                                      HttpServletResponse response,                                                      MissingServletRequestParameterException exception) {         return ApiUtil.fail(exception.getMessage());     }     @ExceptionHandler(ConstraintViolationException.class)     @ResponseStatus(HttpStatus.OK)     public ApiResult methodArgumentNotValidException(HttpServletRequest request,                                                      HttpServletResponse response,                                                      ConstraintViolationException exception) {         System.out.println(exception.getLocalizedMessage());         Iterator


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3