异常的处理在实际的开发中是必不可少的,但是往往也是很多开发人员忽略的地方。
一、默认处理规则
- 默认情况下,spring boot 在出现错误后,会跳转到/error的映射中
- 如果是机器客户端,会返回JSON。浏览器客户端会跳转到error页
- 放在/error下的4xx.html、5xx.html都会被自动解析
二、处理原理
- ErrorMvcAutoConfiguration配置了异常处理的默认规则
- 放入组件DefaultErrorAttributes,客户端返回的数据,都是在这里放入的
- 放入组件BasicErrorController,处理错误请求,默认地址:
- 处理/error的路径骑牛、页面响应返回 new ModelAndView("error", model);
- 容器中就存在一个id为error的View
- 使用BeanNameViewResolver试图解析器,按照beanId寻找试图并解析
- 决定是返回json or 页面就是在这个BasicErrorController 中判断的
-
组件DefaultErrorViewResolver中根据http请求的状态码,寻找对应页面
处理流程
- 执行目标方法,运行期间,有任何异常都会被catch,而且标志当前请求结束。并且用dispatchException封装
- 捕获后,进入试图解析流程,准备页面渲染 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 会保存起来异常
- 处理handler发生的异常,处理完成后返回mv,包含跳转地址和相关数据processHandlerException(request, response, handler, exception);
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { // Success and error responses may use different content types request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); // Check registered HandlerExceptionResolvers... ModelAndView exMv = null; //异常解析器不为空 if (this.handlerExceptionResolvers != null) { //使用异常解析器,看谁能处理当前异常 //默认的解析器有2种 for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { //这里可看到,第一个解析器就是把异常放入request域中 //第二个解析器才进行解析 exMv = resolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } } if (exMv != null) { if (exMv.isEmpty()) { request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null; } // We might still need view name translation for a plain error model... if (!exMv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null) { exMv.setViewName(defaultViewName); } } if (logger.isTraceEnabled()) { logger.trace("Using resolved error view: " + exMv, ex); } else if (logger.isDebugEnabled()) { logger.debug("Using resolved error view: " + exMv); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }
- 遍历全部的handlerExceptionResolver,看谁能处理,就让谁处理
- DefaultErrorAttributes 就是系统异常的默认解析器,在请求域保存异常
private void storeErrorAttributes(HttpServletRequest request, Exception ex) { //把异常信息保存到 request 域中 request.setAttribute(ERROR_ATTRIBUTE, ex); }
- 默认没有能够处理异常的解析器,会把异常抛出
- 如果异常没有被处理,最终底层会转发/error,会被底层的BasicErrorController处理
- 解析错误试图,遍历所有的ErrorVIewResolver,看谁能解析
- 默认的DefaultErrorViewResolver 最后去根据状态码进行匹配页面
- 最终响应页面
三、自定义异常处理
- 自定义错误页 error/404.html、error/5xx.html,如果都没有就触发白页
- @ControllerAdvice + @ExceptionHandler 处理全局异常,调用ExceptionHandlerExceptionResolver
- @ResponseStatus + 自定义异常,调用ResponseStatusExceptionResolver
- String底层异常、例如参数转换异常等底层异常,调用DefaultHandlerExceptionResolver
- 自定义实现HandlerExcptionResolver处理异常,也可以排序,当排序第一的时候,其他异常处理全部失效,也可以作为全局异常处理
- ErrorViewResolver 实现自定义处理异常,只要没人处理的异常都会到这里
- 使用response.sendError,会给/error
- 没有处理分支,也会跳转到/error