1. 概述
上一篇文章Spring Boot系列十 Spring MVC全局异常处理总结介绍了如何在Spring MVC中实现对异常的处理,本文从源码角度理解Spring MVC异常处理原理,主要包括如下内容:
- HandlerExceptionResolver以及常用实现类,理解默认实现HandlerExceptionResolver的用处和源码解读
- Ordered接口及如何自定义HandlerExceptionResolver
- Spring MVC 异常处理HandlerExceptionResolver对象初始化和处理流程的源码解读
2. HandlerExceptionResolver以及常用实现类
2.1 HandlerExceptionResolver接口
HandlerExceptionResolver是一个接口,用于处理网络请求过程中抛出的异常,但是不处理异常本身抛出的异常和视图解析过程中抛出的异常
下图是Spring MVC默认实现的HandlerExceptionResolver类
2.2. HandlerExceptionResolverComposite
Spring Boot启动时会默认注册HandlerExceptionResolverComposite对象。此类只是一个组合类,并不进行真正的异常处理。当他捕获异常时他只是将异常轮询委托给注册到它属性里的上的HandlerExceptionResolver类来处理异常,如果处理的结果不为null,则转给下一个处理
@Override
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
默认注册到HandlerExceptionResolverComposite 的属性有以下3个HandlerExceptionResolver,按照优先级排列如下:
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
下面详细介绍这3个HandlerExceptionResolver的作用
2.3. ExceptionHandlerExceptionResolver
使用@ExceptionHandler注解方法处理异常类,我们之前介绍的使用注解处理异常就有这个类的功劳。默认情况下,这个HandlerExceptionResolver的优先级是最高。
以下是ExceptionHandlerExceptionResolver运行时属性值
属性exceptionHandlerAdviceCache :存储@Controller里@ExceptionHandler的方法
属性exceptionHandlerAdviceCache:存储@ControllerAdvice里@ExceptionHandler的全局方法
处理异常的关键代码
入口doResolveHandlerMethodException方法会通过 getExceptionHandlerMethod获取对应的@ExceptionHandler方法,如果有找到则执行此方法
@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
// 杳找对应的方法@ExceptionHandler
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
....
if (cause != null) {
// 执行异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// 执行异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
....
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
getExceptionHandlerMethod方法:查找特定异常的@ExceptionHandler方法,首先从抛出异常的@Controller类中寻找对应的处理方法,如果没有再从@ControllerAdvice中查找全局的@ExceptionHandler方法,如果找到,则调用这个方法执行处理,否则返回null
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
if (handlerMethod != null) {
// 从抛出异常的@Controller类中自身中寻找对应的处理方法,如果有找到先缓存
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
// 调用方法,返回结果
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
}
// 如果没有再从@ControllerAdvice中查找全局的@ExceptionHandler方法,如果找到,则调用这个方法执行处理
for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
}
}
}
return null;
}
- 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
2.4. ResponseStatusExceptionResolver
使用@ResponseStatus处理异常,将异常转化对应的HTTP的状态码。@ResponseStatus可以定义在Excpetion的子类的类上,也可以定义在被@ExceptionHandler注解的方法上(不过这个需要小心使用,由于ExceptionHandlerExceptionResolver的优先级高,这种方式可能被ExceptionHandlerExceptionResolver覆盖掉)
异常处理入口doResolveException方法会先查找异常上的@ResponseStatus注解信息,如果有ResponseStatus ,则按照ResponseStatus 配置的值处理
//
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
// 获取异常的@ResponseStatus注解信息
ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
try {
// 如果有ResponseStatus ,则按照ResponseStatus 配置的值处理
return resolveResponseStatus(responseStatus, request, response, handler, ex);
}
catch (Exception resolveEx) {
logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);
}
}
else if (ex.getCause() instanceof Exception) {
…
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
根据ResponseStatus 的值设置返回的http状态码和原因
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
int statusCode = responseStatus.code().value();
String reason = responseStatus.reason();
if (!StringUtils.hasLength(reason)) {
// 设置返回的http状态码
response.sendError(statusCode);
}
else {
String resolvedReason = (this.messageSource != null ?
this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
reason);
// 设置返回的http状态码和原因
response.sendError(statusCode, resolvedReason);
}
return new ModelAndView();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
2.5. DefaultHandlerExceptionResolver
默认的HandlerExceptionResolver,将特定异常转化为标准的HTTP的状态码。
详细如下:左边是异常名称,右边是http的状态码
通过代码解释此类行为, 只列出NoSuchRequestHandlingMethodException相关的转换http错误码的代码,表格里其他异常处理类似
异常处理入口doResolveException方法,如果发现异常是NoSuchRequestHandlingMethodException,则调用方法handleNoSuchRequestHandlingMethod
// 对于NoSuchRequestHandlingMethodException进行转化http错误大码
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
try {
if (ex instanceof org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException) {
return handleNoSuchRequestHandlingMethod((org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException) ex,
request, response, handler);
}
else if (ex instanceof HttpRequestMethodNotSupportedException) {
…
}else if
…
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
handleNoSuchRequestHandlingMethod方法返回404错误码和错误信息
protected ModelAndView handleNoSuchRequestHandlingMethod(org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
pageNotFoundLogger.warn(ex.getMessage());
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return new ModelAndView();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2.6. Ordered和实现自定义HandlerExceptionResolver类
每个具体的HandlerExceptionResolver都会实现Ordered接口,来定义执行的顺序,order值越小,越是优先执行。
如果要实现自己HandlerExceptionResolver,只需要满足两个条件:
- 实现接口HandlerExceptionResolver和Ordered
- 使用类似@Component 的注解注解此类,保证spring 启动时创建此类对应的对象即可
详细见上一篇文章Spring Boot系列十 Spring MVC全局异常处理总结
3. Spring MVC异常处理源码解读
3.1. Spring MVC启动时如何初始化HandlerExceptionResolver
Spring mvc启动时,初始化所有HandlerExceptionResolver到Spring 容器中
在Spring boot在启动时,会初始化WebMvcConfigurationSupport 里配置的Bean, 会创建HandlerExceptionResolverComposite对象,此对象包括3个HandlerExceptionResolver,当他捕获异常时,会依次使用这3个HandlerExceptionResolver进行处理,详细如下:
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
…
@Bean
public HandlerExceptionResolver handlerExceptionResolver() {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<HandlerExceptionResolver>();
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
// 添加默认HandlerExceptionResolver类
addDefaultHandlerExceptionResolvers(exceptionResolvers);
}
extendHandlerExceptionResolvers(exceptionResolvers);
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
// 添加默认HandlerExceptionResolverComposite及注册到此对象中的HandlerExceptionResolverComposite
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
// 创建 ExceptionHandlerExceptionResolver()
ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());
exceptionHandlerResolver.setMessageConverters(getMessageConverters());
exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
exceptionHandlerResolver.setResponseBodyAdvice(
Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice()));
}
exceptionHandlerResolver.setApplicationContext(this.applicationContext);
exceptionHandlerResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionHandlerResolver);
// 创建ResponseStatusExceptionResolver
ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
responseStatusResolver.setMessageSource(this.applicationContext);
exceptionResolvers.add(responseStatusResolver);
// 创建DefaultHandlerExceptionResolver
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
return new ExceptionHandlerExceptionResolver();
}
}
- 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
3.2. DispatcherServlet
入口类,是一个Servlet,是所有请求的分发点
以下源码都在此类
3.2.1. 初始化
DispatcherServlet在初始化时会触发onRefresh()方法,此方法会调用initStrategies方法,完成整个DispatcherServlet的初始化工作,其中initHandlerExceptionResolvers()会初始化HandlerExceptionResolvers对象
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
// 初始化HandlerExceptionResolvers对象
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
从Spring容器的ApplicationContext中找出所有HandlerExceptionResolvers对象,将保存到对象属性handlerExceptionResolvers 中。
从这里我们也知道如果要在spring mvc中插入自己的HandlerExceptionResolver也比较简单,只需要类实现接口HandlerExceptionResolver和Ordered,使用类似@Component 的注解注解此类即可
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
if (this.detectAllHandlerExceptionResolvers) {
// 从Spring容器的ApplicationContext中找出所有HandlerExceptionResolvers对象
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
// 将Map转化为List,保存到属性handlerExceptionResolvers 中
this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
// 对HandlerExceptionResolvers使用据order接口里值进行排序
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
到此初始化工作完成
3.2.2. 异常处理流程
当执行@RequestMapping抛出异常,会进入异常处理流程
所有的doPost, doGet等do*的方法都会执行到以下方法:找到真正业务的处理逻辑,并进行处理。
下面的代码是找到本次请求真正要处理的HandlerAdapter 对象,并进行处理,最后调用processDispatchResult对结果进行处理,这是我们关心的内容
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
…
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理最后的方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
…
}
- 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
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
处理业务执行的结果,处理结束可能是 ModelAndView,也可能是Exception。如果结果是Exception,就需要通过本文提到的HandlerExceptionResolver转化为ModelAndView。然后根据ModelAndView将结果返回给请求方
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
// 如果返回值是异常,通过本文提到的HandlerExceptionResolver转化为ModelAndView
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
// 根据ModelAndView执行后续操作
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
…
}
- 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
当异常发生时, DispatcherServlet会轮询调用HandlerExceptionResolver,直到异常被转化为ModelAndView
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
….
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
此时异常处理完毕
本文章转自https://blog.youkuaiyun.com/hry2015/article/details/78957949