该系列上一篇 : DispatcherServlet 请求处理主逻辑 : 2. 选择 Handler 对应的 HandlerAdapter
本文代码版本 : spring-webmvc-5.1.5.RELEASE
DispatcherServlet
请求处理主逻辑(#doDispatch
)找到针对当前请求的Handler
以及HandlerAdapter
之后,就是使用HandlerAdapter
执行该Handler
从而处理当前请求了。相应的调用语句如下:
// 1. 这里 mappedHandler 是所找到的 Handler, 类型为 HandlerExecutionChain
// 相当于 : N HandlerInterceptor 包裹一个 Handler
// 2. 这里 mv 是一个类型为 ModelAndView 的对象
// 3. 这里 ha 是所找到的 HandlerAdapter
// 在调用 Handler 处理请求之前,应用各个 HandlerInterceptor 的前置拦截处理逻辑 preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 各个HandlerInterceptor#preHandle前置处理逻辑应用完成且都返回true,
// 现在需要通过 ha 调用相应的 Handler 了
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 注意 : 这里 mv 存在为 null 的情况,比如开发人员在控制器方法上使用了
// @ResponseBody, 则最终返回给客户端的是JSON/XML之类的数据,而不需要
// 视图渲染,此时 mv 就会为 null。不过这种情况下虽然 mv 为null,当前
// 处理流程并不会结束,还是会继续,但下面的步骤会考虑该情况进行相应的处理。
// 这里有一些异步处理相关的代码,为了方便描述请求处理主逻辑,这里将其删除
// ...
// 通过 ha 调用相应的 Handler 处理请求结束,现在对处理结果 mv 做个微调 :
// 如果 mv 中不含视图,并且能找到一个处理该请求的默认视图,则将该默认视图
// 设置到 mv 中
applyDefaultViewName(processedRequest, mv);
// 现在已经使用 Handler 处理了请求,结果是 mv 并且已经做了是否应用默认视图的微调,
// 现在需要应用各个 HandlerInterceptor 的后置拦截处理逻辑 postHandle 了
mappedHandler.applyPostHandle(processedRequest, response, mv);
概括来讲,上面使用HandlerAdapter
执行该Handler
处理请求的逻辑主要分为4个小步骤 :
HandlerInterceptor
的前置拦截处理逻辑preHandle
;HandlerAdapter
执行Handler
以处理请求,返回结果ModelAndView mv
;mv
做是否应用默认视图的调整;HandlerInterceptor
的后置拦截处理逻辑postHandle
;接下来,我们继续深入,看看以上各个小步骤都做了些什么 。
// 类 HandlerExecutionChain 代码片段
/**
* Apply preHandle methods of registered interceptors.
* @return true if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
Handler
处理请求这个小步骤的逻辑是使用HandlerAdapter
执行Handler
以处理请求,返回结果ModelAndView mv
。至于执行的细节,要取决于HandlerAdapter
实现类。如果对此想了解更多,可以参考一下Spring MVC HandlerAdapter : 控制器方法适配器 RequestMappingHandlerAdapter,这是一个针对控制器方法的HandlerAdapter
,换句话讲,开发人员通过@Controller
注解定义的类中使用了@RequestMapping
注解这样的方法,都是通过RequestMappingHandlerAdapter
来执行的。
/**
* Do we need view name translation?
*/
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
// 类 HandlerExecutionChain 代码片段
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
通过上面的分析,我们可以看到DispatcherServlet
请求处理主逻辑是如何使用各个组件协调完成请求处理的。并且,使用HandlerAdapter
执行Handler
处理完请求之后,输出的结果是一个ModelAndView mv
,该对象持有两部分信息 :
Model
;View
,可能是View
对象,也可能是View
的名称;DispatcherServlet
请求处理主逻辑下一步骤消费用于生成最终返回给客户端的视图数据。上面的分析仅限于没有遇到异常的情况,那么,如果遇到了异常,又会怎样呢?实际上,以上步骤调用被一个try
块包围,一旦遇到Exception
,或者是Throwable
,最终都会catch
捕获并包装成一个异常对象(变量名称使用dispatchException
),该异常对象dispatchException
和上面提到的mv
一样,也会提供给下一步骤的处理逻辑使用。