该系列的下一篇 : DispatcherServlet 请求处理主逻辑 : 2. 选择 Handler 对应的 HandlerAdapter
一个Handler
可以理解为处理用户请求的方法。此方法可能最终对应一个开发人员定义的Controller
方法,也可能是Spring MVC
框架根据开发人员配置自动提供的某个方法。Handler
处理请求结果一般会是一个ModelAndView
对象。该对象会被接下来的步骤进行其他处理,比如根据视图名称渲染视图,进行页面调转等等。Handler
处理请求过程中也可能发生异常,这些异常信息也会被后续步骤用于渲染错误页面。
DispatcherServlet
处理请求时,选择Handler
是根据请求找出能处理该请求的Handler
的过程。该过程只负责找出能处理该请求的Handler
,并不调用它处理该请求。该过程的逻辑实现是DispatcherServlet
的方法HandlerExecutionChain getHandler(HttpServletRequest request)
。调用是DispatcherServlet
方法void doDispatch(HttpServletRequest request, HttpServletResponse response)
中的语句mappedHandler = getHandler(processedRequest)
。可以认为这一过程是DispatcherServlet
处理请求的第一步。
应用启动过程中,根据开发人员提供的配置,DispatcherServlet
组件的属性handlerMappings
已经被填充,这是一个List<HandlerMapping>
。根据该属性,DispatcherServlet
可以根据一个请求中的信息查找能够处理它的Handler
。
可以将handlerMappings
理解成一个查找表,保存的是<url pattern,handler 对象>
对儿,当一个请求到达时,DispatcherServlet
会获取该请求的请求路径,从该查找表中查找到相应的Handler
。比如对于一个映射到Controller
方法上的请求,这里的Handler
其实是一个HandlerMethod
。
参考文章 : Spring MVC : WebMvcConfigurationSupport 中定义的 HandlerMapping 组件
getHandler
方法正是使用了handlerMappings
查找到达请求的handler
的 ,代码逻辑如下所示 :
/**
* Return the HandlerExecutionChain for this request.
* Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or null if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
如上所示,方法getHandler
的逻辑简单直白,就是遍历handlerMappings
中每个HandlerMapping
以查找能处理指定请求的Handler
,如果找到了合适的Handler
,则包装成HandlerExecutionChain
形式返回。如果没有找到,则返回null
。
这里为什么要包装成HandlerExecutionChain
,而不是直接返回Handler
本身呢?这是因为Spring MVC
的拦截器机制有可能需要向目标Handler
包裹一些HandlerInterceptor
。常见的一些HandlerInterceptor
有ConversionServiceExposingInterceptor
,ResourceUrlProviderExposingInterceptor
。
DispatcherServlet#doDispatch
使用getHandler
找不到处理当前请求的handler
是,会怎么做呢 ?
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
从上面代码可见,这种情况下DispatcherServlet#doDispatch
会调用方法noHandlerFound
,然后就直接返回了,不再继续下面的其他步骤。方法noHandlerFound
的实现逻辑如下 :
/**
* No handler found -> set appropriate HTTP response status.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if preparing the response failed
*/
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
}
if (this.throwExceptionIfNoHandlerFound) {
// 如果属性 throwExceptionIfNoHandlerFound 为 true,则抛出异常 NoHandlerFoundException
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
// 如果属性 throwExceptionIfNoHandlerFound 为 false , 则 response.sendError(404)
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
从以上代码可见,根据开发人员提供的配置this.throwExceptionIfNoHandlerFound
的不同,noHandlerFound
会抛出异常NoHandlerFoundException
或者向响应发送错误404
。缺省情况下,this.throwExceptionIfNoHandlerFound
为false
。
总结
- 本文梳理了
DispatcherServlet
针对到达的请求获取能处理该请求的Handler
的过程,包含如下要点 :Handler
是什么 ?- 这些
Handler
从哪里来 ? Handler
为什么要用HandlerExecutionChain
形式包装?- 如何判断一个
Handler
是否支持处理某个请求 ? - 如果找不到能处理该请求的
Handler
会怎么样 ?