spring boot 中异常处理

异常的处理在实际的开发中是必不可少的,但是往往也是很多开发人员忽略的地方。

一、默认处理规则

  • 默认情况下,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

猜你喜欢

转载自blog.csdn.net/liming0025/article/details/120797862
今日推荐