Spring与MVC(六)

在上一篇文章Spring与MVC(五)中,我们分析了Spring MVC是如何处理方法参数以及响应返回值。在这篇文章中,我们来分析一下Spring MVC如何处理异常。

  1. 程序在DispatcherServlet.doDispatch方法中调用ha.handle执行相应的函数,这时抛出异常,异常赋值给dispatchException
  2. 调用processDispatchResult方法处理捕获的异常。

processHandlerException

DispatcherServlet.processDispatchResult方法的processHandlerException中调用HandlerExceptionResolverComposite.resolveException,遍历所有的异常处理器(实现HandlerExceptionResolver接口),处理异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
if (resolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : resolvers) {
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
return mav;
}
}
}
return null;
}

其中默认的异常处理器有3个:

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

如果经过这三个异常处理器处理之后得到的ModelAndView为null,则继续抛出异常,最后由servlet容器来处理

DefaultHandlerExceptionResolver

继承自AbstractHandlerExceptionResolver,主要对一些特殊的异常进行处理,比如NoSuchRequestHandlingMethodException, HttpRequestMethodNotSupportedException, HttpMediaTypeNotSupportedException, HttpMediaTypeNotAcceptableException

ResponseStatusExceptionResolver

继承自AbstractHandlerExceptionResolver,主要在异常父类中找到@ResponseStatus注解,然后使用这个注解的属性进行处理

ExceptionHandlerExceptionResolver

继承自AbstractHandlerMethodExceptionResolver,该类主要处理Controller中用@ExceptionHandler注解定义的方法,大多数异常处理都是由该类操作。

我们来看看ExceptionHandlerExceptionResolver在用户controller方法调用抛出异常时是如何工作的:

DispatcherServlet.doDispatch方法中执行ha.handle调用用户的controller方法,抛出异常后被catch捕获,将异常赋值给dispatchException变量。

然后调用processDispatchResult方法来处理执行结果或者抛出的异常。当然这里关注的是处理抛出的异常:

1
2
3
4
5
6
7
8
9
10
11
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

我们看到,如果我们的异常不是ModelAndViewDefinitionException,则调用processHandlerException:遍历handlerExceptionResolvers,其中其中的resolveException遍历所有的HandlerExceptionResolver处理异常。

来看ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolve

它的resolveException方法在父类AbstractHandlerExceptionResolver中。它的主要工作是调用doResolveException处理异常并返回ModelAndViewdoResolveException调用doResolveHandlerMethodExceptiondoResolveHandlerMethodExceptionExceptionHandlerExceptionResolver中实现:

  1. 首先调用getExceptionHandlerMethod获取异常处理的方法exceptionHandlerMethod

    • 先获取controller方法的类型
    • 根据controller方法的类型获取处理异常的方法,具体的过程在ExceptionHandlerMethodResolver类的构造函数中

      1
      2
      3
      4
      5
      6
      7
      public ExceptionHandlerMethodResolver(Class<?> handlerType) {
      for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
      for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
      addExceptionMapping(exceptionType, method);
      }
      }
      }

      MethodIntrospector.selectMethods方法中获取所有controller方法对应的异常处理方法,调用detectExceptionMappings获取异常处理函数的处理的异常类型,然后将异常类型与异常处理方法的键值对添加到ExceptionHandlerMethodResolvermappedMethods中。

    • 调用ExceptionHandlerMethodResolver.resolveMethod方法根据异常获取异常处理方法

  2. 调用exceptionHandlerMethodinvokeAndHandle方法执行异常处理方法

    执行异常处理方法的过程与执行普通的controller方法一致

回到DispatcherServletprocessDispatchResult方法,如果调用processHandlerException方法返回的ModelAndView不为null则渲染页面。