该系列的下一篇 : 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会怎么样 ?
本文详细解析了SpringMVC框架中DispatcherServlet如何选取适合处理请求的Handler,包括Handler的概念、来源,以及为何需要通过HandlerExecutionChain包装。同时,探讨了在未找到合适Handler时的处理策略。
2960

被折叠的 条评论
为什么被折叠?



