深圳幻海软件技术有限公司 欢迎您!

Spring MVC核心功能异常处理机制原理详解

2023-02-28

概述如果在请求映射期间发生异常或从请求处理程序(例如@Controller)抛出异常,DispatcherServlet将委托给HandlerExceptionResolver下表列出了可用的HandlerExceptionResolver实现:HandlerExceptionResolver描述S

概述

如果在请求映射期间发生异常或从请求处理程序(例如@Controller)抛出异常,DispatcherServlet将委托给HandlerExceptionResolver

下表列出了可用的HandlerExceptionResolver实现:

HandlerExceptionResolver

描述

SimpleMappingExceptionResolver

异常类名和错误视图名之间的映射。用于在浏览器应用程序中渲染错误页面。

DefaultHandlerExceptionResolver

解析Spring MVC引发的异常,并将其映射为HTTP状态码。另见可选的ResponseEntityExceptionHandler和REST API异常。

ResponseStatusExceptionResolver

使用@ResponseStatus注解解析异常,并根据注解中的值将异常映射为HTTP状态码。

ExceptionHandlerExceptionResolver

通过在@Controller或@ControllerAdvice类中调用@ExceptionHandler方法来解决异常。

异常解析器链

你可以在Spring配置中声明多个HandlerExceptionResolver

HandlerExceptionResolver的约定规定它可以返回:

  • 指向错误视图的ModelAndView
  • 如果异常是在解析器中处理的,则返回空的ModelAndView
  • 如果异常仍然未解决,则为null,以便后续的解析器尝试,如果异常在最后仍然存在,则允许它向上冒泡到Servlet容器。

MVC配置自动为默认的Spring MVC异常、@ResponseStatus注解的异常以及@ExceptionHandler方法声明了内置的解析器。

容器错误页配置

如果任何HandlerExceptionResolver都无法解决异常,因此需要传播,或者响应状态被设置为错误状态(即4xx、5xx), Servlet容器可以在HTML中渲染一个默认的错误页面。要定制容器的默认错误页面,可以在web.xml中声明错误页面映射。如下面的例子所示:​

<error-page>
  <location>/error</location>
</error-page>
  • 1.
  • 2.
  • 3.

根据前面的示例,当出现异常冒泡或响应具有错误状态时,Servlet容器将在容器中向配置的URL(例如/error)发送错误。然后由DispatcherServlet处理,可能会将其映射到一个@Controller,这个@Controller可以实现为返回一个带有模型的错误视图名称,或者渲染一个JSON响应,如下面的例子所示:​

@RestController
public class ErrorController {


  @RequestMapping(path = "/error")
  public Map<String, Object> handle(HttpServletRequest request) {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("status", request.getAttribute("javax.servlet.error.status_code"));
    map.put("reason", request.getAttribute("javax.servlet.error.message"));
    return map;
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

错误处理原理

public class DispatcherServlet {
  // 取得容器中所有的异常解析器
  private List<HandlerExceptionResolver> handlerExceptionResolvers;
  protected void initStrategies(ApplicationContext context) {
    // 初始化异常解析器
    initHandlerExceptionResolvers(context);
  }
  private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;
    if (this.detectAllHandlerExceptionResolvers) {
      // 获取容器中所有的异常解析器
      Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
      if (!matchingBeans.isEmpty()) {
        this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
        AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
      }
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

发生异常后逻辑处理​

public class DispatcherServlet {
  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  }
  private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 
    @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, 
    @Nullable Exception exception) throws Exception {


    boolean errorView = false;
    // 判断当前调用是否发生了异常
    if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
        mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      } else {
        // 获取当前处理句柄HandlerMethod
        Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        // 处理异常
        mv = processHandlerException(request, response, handler, exception);
        errorView = (mv != null);
      }
    }
    if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
    }
  }
  protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, 
    @Nullable Object handler, Exception ex) throws Exception {
    request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
      // 遍历在上面初始化查找到的所有异常解析器
      for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
        // 执行异常解析
        exMv = resolver.resolveException(request, response, handler, ex);
        if (exMv != null) {
          break;
        }
      }
    }
    if (exMv != null) {
      // ...
      return exMv ;
    }
    // 如果所有的异常解析器都返回null,则直接抛出异常,该异常会被Servlet进行处理
    throw ex ;
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.

异常解析器默认配置

public class WebMvcAutoConfiguration {
  @Configuration(proxyBeanMethods = false)
  @EnableConfigurationProperties(WebProperties.class)
  public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
  }  
}
public class WebMvcConfigurationSupport {
  @Bean
  public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
    // 处理自定义异常解析器的配置
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
      // 如果没有异常解析器,那么添加默认的异常解析器
      addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
    }
    extendHandlerExceptionResolvers(exceptionResolvers);
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    // 设置高优先级,Composite成为统一的入口
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);
    return composite;
  }
  // 默认配置
  protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers, ContentNegotiationManager mvcContentNegotiationManager) {
    ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
    exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
    exceptionHandlerResolver.setMessageConverters(getMessageConverters());
    exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
    exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
    if (jackson2Present) {
      exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }
    if (this.applicationContext != null) {
      exceptionHandlerResolver.setApplicationContext(this.applicationContext);
    }
    exceptionHandlerResolver.afterPropertiesSet();
    exceptionResolvers.add(exceptionHandlerResolver);
    ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
    responseStatusResolver.setMessageSource(this.applicationContext);
    exceptionResolvers.add(responseStatusResolver);
    exceptionResolvers.add(new DefaultHandlerExceptionResolver());
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.

完毕!!!