在上一篇文章Spring与MVC(五)中,我们分析了Spring MVC是如何处理方法参数以及响应返回值。在这篇文章中,我们来分析一下Spring MVC如何处理异常。
- 程序在
DispatcherServlet.doDispatch
方法中调用ha.handle
执行相应的函数,这时抛出异常,异常赋值给dispatchException
。 - 调用
processDispatchResult
方法处理捕获的异常。
processHandlerException
在DispatcherServlet.processDispatchResult
方法的processHandlerException
中调用HandlerExceptionResolverComposite.resolveException
,遍历所有的异常处理器(实现HandlerExceptionResolver
接口),处理异常
1 | public ModelAndView resolveException(HttpServletRequest request, |
其中默认的异常处理器有3个:
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- 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 | if (exception != null) { |
我们看到,如果我们的异常不是ModelAndViewDefinitionException
,则调用processHandlerException
:遍历handlerExceptionResolvers
,其中其中的resolveException
遍历所有的HandlerExceptionResolver
处理异常。
来看ExceptionHandlerExceptionResolver
它的resolveException
方法在父类AbstractHandlerExceptionResolver
中。它的主要工作是调用doResolveException
处理异常并返回ModelAndView
。doResolveException
调用doResolveHandlerMethodException
,doResolveHandlerMethodException
在ExceptionHandlerExceptionResolver
中实现:
首先调用
getExceptionHandlerMethod
获取异常处理的方法exceptionHandlerMethod
- 先获取controller方法的类型
根据controller方法的类型获取处理异常的方法,具体的过程在
ExceptionHandlerMethodResolver
类的构造函数中1
2
3
4
5
6
7public 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
获取异常处理函数的处理的异常类型,然后将异常类型与异常处理方法的键值对添加到ExceptionHandlerMethodResolver
的mappedMethods
中。调用
ExceptionHandlerMethodResolver.resolveMethod
方法根据异常获取异常处理方法
调用
exceptionHandlerMethod
的invokeAndHandle
方法执行异常处理方法执行异常处理方法的过程与执行普通的controller方法一致
回到DispatcherServlet
的processDispatchResult
方法,如果调用processHandlerException
方法返回的ModelAndView
不为null则渲染页面。