本系列文章皆在分析SpringMVC
的核心组件和工作原理,让你从SpringMVC
浩如烟海的代码中跳出来,以一种全局的视角来重新审视SpringMVC
的工作原理.
前言
截止到目前, 我们沿着dispatch
方法调用链逐步对DispatcherServlet
中处理http
请求的流程进行了分析。
本次,我们将分析doDispatch
调用链中最后一个方法processDispatchResult
。同时,与该方法相关的组件为HandlerExceptionResolver
。
processDispatchResult
相关逻辑
在分析讨论processDispatchResult
讨论之前,我们先看看该方法在doDispatch
调用流程。
doDispatch
方法
java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = null;
Exception dispatchException = null;
// 上传逻辑处理
processedRequest = checkMultipart(request);
// 寻找对应处理器
mappedHandler = getHandler(processedRequest);
// ......省略其他无关方法
// 本文重点关注方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
可以看到,对于processDispatchResult
方法而言,其会需要五个参数,其中processedRequest,mappedHandler
这个我们在之前文章都进行过详细介绍。具体可以参考:
- SpringMVC流程分析(三):SpringMVC中处理上传请求的秘密 url: juejin.cn/post/725848...
- SpringMVC流程分析(四):SpringMVC中如何为一个请求选择合适的处理器 url: juejin.cn/post/726125...
除了这几个参数外,我们注意到其还会传入一个Exception
对象。这就很奇怪了,因为在java
中我们对于异常通常会通过try-cath
来进行处理,此处将Exception
传入方法又是作何处理?
接下来我们便具体分析processDispatchResult
内部的相关逻辑。其相关代码如下:
java
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// ......省略其他无关代码
if (exception != null) {
// 异常为ModelAndViewDefiningException类型,直接从exception中获取异常的View视图
if (exception instanceof
ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
// 非ModelAndViewDefiningException类型
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// 生成modelview对象
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// ......省略其他无关代码
}
可以看到processDispatchResult
的主要任务在于处理doDispatch
处理过中出现的异常信息。 进一步,对于传入processDispatchResult
的异常会根据异常类型的不同来选取不同的处理方式。
具体而言,如果异常为ModelAndViewDefiningException
类型,直接从Exception
中获取异常的ModelAndView
进行返回;
如果异常信息为非ModelAndViewDefiningException
的异常信息,则会调用processHandlerException
进行处理。其相关逻辑如下:
java
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex)
throws Exception {
ModelAndView exMv = null
if (this.handlerExceptionResolvers != null) {
// 遍历所有的HandlerExceptionResolver处理器
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
// 解析异常,⽣成 ModelAndView 对象
exMv = resolver.resolveException(request, response, handler, ex);
// 如果解析异常结果不为null则退出遍历
if (exMv != null) {
break;
}
}
}
// .... 省略其他无关代码
// 情况⼀,⽣成了 ModelAndView 对象,进⾏返回
if (exMv != null) {
// ... 省略其他无关代码
return exMv;
}
// 情况⼆,未⽣成 ModelAndView 对象,则抛出异常
throw ex;
}
可以看到对于processHandlerException
方法,其同我们之前讲过的getHanlder、getHandlerAdapter
有着类似的逻辑。、
其会遍历容器
中所有的 HandlerExceptionResolver
。然后依次选取并进行判断,如果某⼀个HandlerExceptionResolver
可以成功处理传入的异常
信息,则返回 ModelAndView
对象。
HandlerExceptionResolver
类结构
可以看到在上述方法调用过程中,方法内部会用到一个HandlerExceptionResolver
的组件信息,接下来我们便看看这个HandlerExceptionResolver
在SpringMVC
中究竟会完成哪些任务。其结构如下所示:
通过上述类图可以看到,对于HandlerExceptionResolver
而言,其有四个默认的实现类。事实上,这些HandlerExceptionResolver
的具体实现类,会在SpringMVC
初始化时也会默认加载,而无需我们配置。而有关HandlerExceptionResolver
初始化的逻辑在DispatcherServlet
中的initStrategies
的方法内部。
进一步,HandlerExceptionResolver
内部所定义方法信息如下所示:
java
public interface HandlerExceptionResolver {
/**
* 解析异常信息
* */
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
超级基类:AbstractHandlerExceptionResolver
通过上述的uml
类图,我们注意到AbstractHandlerExceptionResolver
会实现HandlerExceptionResolver
接口,而在该接口内,只有一个resolveException
方法,所以我们只需关注AbstractHandlerExceptionResolver
中有关方法resolveException
实现即可。其相关逻辑如下:
java
public ModelAndView resolveException(
HttpServletRequest request,
HttpServletResponse response, @Nullable Object handler, Exception ex) {
// 判断当前处理器能否解析当前handler信息
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
// 此处doResolveException 为一个抽象方法,具体逻辑交给子类实现,以完成对异常信息的解析和处理
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
// ... 省略其他方法
// 日志记录异常方法信息
logException(ex, request);
}
return result;
}
else {
// 如果无法解析则返回一个null
return null;
}
}
可以看到,其首先会调⽤ shouldApplyTo
,判断异常处理器是否可以应⽤当前处理器
,此时,如果可以应⽤,则调⽤ doResolveExceptio
抽象⽅法,执⾏解析异常,并返回ModelAndView
对象,否则则直接返回 null
。
看到此处的null
你一定会更加深刻理解之前processHandlerException
为什么可以通过判断返回值是否为null
便可以结束对于HandlerExceptionResolver
的遍历。
默认实现:DefaultHandlerExceptionResolver
对于HandlerExceptionResolver
的实现类,我们仅选取其中的DefaultHandlerExceptionResolver
来进行分析,看看其内部的doResolveException
都定义了哪些处理逻辑。
(注:对于ResponseStatusExceptionResolver
其主要对注解@ResponseStatus
注解标注信息进行封装,而ExceptionHandlerExceptionResolver
则是对注解@ExceptionHanlder
标注的方法进行解析,内部处理逻辑同RequestMappingHandlerAdapter
类似,感兴趣的读者可独自进行分析。)
java
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
//
if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported(
(HttpRequestMethodNotSupportedException) ex, request, response, handler);
}
// 省略其他相似代码
else if (ex instanceof ConversionNotSupportedException) {
return handleConversionNotSupported(
(ConversionNotSupportedException) ex, request, response, handler);
}
// ...省略其他相似代码
}
// ...省略其他相似代码
return null;
}
DefaultHandlerExceptionResolver
内部的doResolveException
方法逻辑并不复杂,无非就是根据不同的异常类型,然后设置响应码和错误信息。例如 当转换类型不支持时,方法handleConversionNotSupported
内部逻辑如下:
java
protected ModelAndView handleConversionNotSupported(ConversionNotSupportedException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
sendServerError(ex, request, response);
return new ModelAndView();
}
可以看到上述方法会先通过sendServerError
设定错误相应的状态码,然后返回一个空的ModelAndView
对象。
这样做的目的是避免其被后续的 ViewResolver
所处理。
总结
本⽂对 Spring MVC
中的 HandlerExceptionResolver
组件进⾏分析,处理器异常解析器,将处理器执⾏时发⽣的异常解析(转换)成对应的 ModelAndView
对象。
同时,HandlerExceptionResolver
的实现类没有特别多,在 Spring MVC
和 Spring Boot
中,默认情况下都有三种 HandlerExceptionResolver
实现类,其分别为:
-
ExceptionHandlerExceptionResolve
:处理基于@ExceptionHandler
配置HandlerMethod
的HandlerExceptionResolver
实现 -
ResponseStatusExceptionResolve
: 处理基于@ResponseStatus
提供错误响应的HandlerExceptionResolver
实现类 -
DefaultHandlerExceptionResolver
: 默认HandlerExceptionResolver
实现类。
进一步,我们对其中的 DefaultHandlerExceptionResolver
进行了详细的分析,作为默认 HandlerExceptionResolver
实现类,其会针对各种异常, 设置错误响应码。