RuoYi-Vue 前后端分离版代码浅析-全局异常捕捉
前言
本节介绍RuoYi-Vue
的模块中如何使用全局异常捕捉,全局异常捕捉对于每个项目而言都是必须的,在它的帮助下我们可以集中精力进行业务代码的开发,不用考虑异常的处理,所有的异常都可以由这里进行捕获之后统一处理。 ruoyi
中使用的是@RestControllerAdvice
,它相对@ControllerAdvice
,减少了@ResponseBody
注解,只需要在具体方法上添加@ExceptionHandler
注解,对于我们现在前后端分离的代码而言,直接使用@RestControllerAdvice
即可。
@RestControllerAdvice
一般来说我们的RestControllerAdvice
必须的处理方法有以下几种
自定义验证异常
它主要是处理@Validated错误的,用来将返回的验证消息格式成我们已经写好的返回类。MethodArgumentNotValidException
是BindException
的子类。
/** * 自定义验证异常 */ @ExceptionHandler(BindException.class) public AjaxResult handleBindException(BindException e) { log.error(e.getMessage(), e); String message = e.getAllErrors().get(0).getDefaultMessage(); return AjaxResult.error(message); } /** * 验证异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error(e.getMessage(), e); String message = e.getBindingResult().getFieldError().getDefaultMessage(); return AjaxResult.error(message); } 复制代码
这里还有另外一个异常ConstraintViolationException
也可以进行捕捉自定义验证异常,不过是在 javax.validation
包中,而不是像上面两个异常在spring中,上面两个异常其实也不在一个包里。
业务异常
我们一般也会自定义一个业务异常,当逻辑上有问题时会使用这个异常向外抛出错误
/** * 业务异常 */ @ExceptionHandler(ServiceException.class) public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) { log.error(e.getMessage(), e); Integer code = e.getCode(); return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); } 复制代码
系统异常
这一般是用来进行兜底的,如果其他的都拦不住的时候就要用系统异常来进行拦截了。
/** * 系统异常 */ @ExceptionHandler(Exception.class) public AjaxResult handleException(Exception e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',发生系统异常.", requestURI, e); return AjaxResult.error(e.getMessage()); } 复制代码
多个异常调用顺序
AbstractHandlerMethodExceptionResolver 是处理异常的抽象基础类,它是对HandlerExceptionResolver
这个接口的实现,继承了AbstractHandlerExceptionResolver
处理位置是
中的doResolveHandlerMethodException
在获取getExceptionHandlerMethod
这里会根据错误类型来找对应能处理的方法
@Nullable public Method resolveMethodByThrowable(Throwable exception) { Method method = resolveMethodByExceptionType(exception.getClass()); if (method == null) { Throwable cause = exception.getCause(); if (cause != null) { method = resolveMethodByThrowable(cause); } } return method; } @Nullable public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) { Method method = this.exceptionLookupCache.get(exceptionType); if (method == null) { method = getMappedMethod(exceptionType); this.exceptionLookupCache.put(exceptionType, method); } return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null); } 复制代码
这里的重点是用getMappedMethod
来使用ExceptionDepthComparator
这个比较器来找到最接近的比较器对这个Throwable
来进行处理。
可以看到在getDepth这里是使用了一个递归来查深度的,
多个@RestControllerAdvice加载顺序
如果我们引用的别的api包中已经有了@RestControllerAdvice,每次都调用了它的处理,我们想改为自己的,可以使用@Order(Ordered.HIGHEST_PRECEDENCE)
注解,将我们的处理类提升,但是一般来说不要这样做,可能会将别人的处理类覆盖掉,所以一定要做的话一定要将我们的处理的Exception写的越细越好,这样不会影响原有逻辑,防止处理了别人的逻辑。
作者:临时营地
链接:https://juejin.cn/post/7029158877224501279