SpringMVC doDispatch方法的基本思路梳理
Posted HelloWorld_EE
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC doDispatch方法的基本思路梳理相关的知识,希望对你有一定的参考价值。
SpringMVC doDispatch方法的基本思路梳理
在分析之前,先介绍一个类
1、HandlerExecutionChain
public class HandlerExecutionChain
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex;
该类中主要包括了一个用于处理request的handler,以及和request相关的一系列拦截器interceptors.
进入正题,doDispatch方法的代码如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try
try
ModelAndView mv = null;
Exception dispatchException = null;
try
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
/*
分析1,其目的是:得到包含了用于处理request的HandlerMethod、Interceptors。
*/
mappedHandler = this.getHandler(processedRequest, false);
//如果没有,则说明没有处理器能够处理该request,因此返回404错误。
if(mappedHandler == null || mappedHandler.getHandler() == null)
this.noHandlerFound(processedRequest, response);
return;
/*
分析2,其目的是:得到与Handler匹配的HandlerAdapter。
*/
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if(isGet || "HEAD".equals(method))
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if(this.logger.isDebugEnabled())
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet)
return;
/*
分析3:执行所有注册拦截器的preHandler方法
*/
if(!mappedHandler.applyPreHandle(processedRequest, response))
return;
try
//前面的第一步就是找到用于处理request的handler,到这里才真正意思上的执行用于处理request的handler方法。具体后面进行分析
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
finally
if(asyncManager.isConcurrentHandlingStarted())
return;
this.applyDefaultViewName(request, mv);
/*
分析4:执行所有注册拦截器的postHandler方法
*/
mappedHandler.applyPostHandle(processedRequest, response, mv);
catch (Exception var27)
dispatchException = var27;
this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
catch (Exception var28)
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var28);
catch (Error var29)
this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var29);
finally
if(asyncManager.isConcurrentHandlingStarted())
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
else
if(multipartRequestParsed)
this.cleanupMultipart(processedRequest);
doDispatch方法主要逻辑如下:
1)找到用于处理request的handler和interceptors,构造成一个HandlerExecutionChain.
2) 找到与用于处理request的handler相匹配的HandlerAdapter,目的是:后续将使用此HandlerAdapter来执行handler。
3)执行所有注册拦截器的preHandler方法
4)真正意思上的执行用于request的handler方法,得到ModelAndView
5)倒序执行所有注册拦截器的postHandler方法。
6)解析并返回ModelAndView
主要是理清思路,本篇博文将依次分析除第4点之外的几点。
分析1:找到用于处理request的handler和interceptors,构造成一个HandlerExecutionChain.
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception
for (HandlerMapping hm : this.handlerMappings)
if (logger.isTraceEnabled())
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null)
return handler;
return null;
该函数的功能为:遍历DispatcherServlet中的private List handlerMappings; 看哪个HandlerMapping能够处理该request,则返回由
AbstractHandlerMapping.java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception
Object handler = getHandlerInternal(request);//第一步
if (handler == null)
handler = getDefaultHandler();
if (handler == null)
return null;
// Bean name or resolved handler?
if (handler instanceof String)
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
//第二步
return getHandlerExecutionChain(handler, request);
此方法的主要逻辑如下:
第一步:根据request中的url得到相应的handler,此handler为HandlerMethod的实例。
第二步:将第一步得到的HandlerMethod作为参数实例化一个HandlerExecutionChain对象。在实例化过程中,还需要得到与request相关的Interceptors。
下面将逐一的分析这两步所涉及到具体实现。
先看第一步:根据request中的url得到相应的handler,此handler为HandlerMethod的实例
getHandlerInternal方法的代码如下:
AbstractHandlerMethodMapping.java
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled())
logger.debug("Looking up handler method for path " + lookupPath);
//得到与request相关的handlerMethod,如何得到的,就是在Map中来寻找
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled())
if (handlerMethod != null)
logger.debug("Returning handler method [" + handlerMethod + "]");
else
logger.debug("Did not find handler method for [" + lookupPath + "]");
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
getHandlerInternal函数的功能:就是在Map中寻找HandlerMethod方法。Map中保存的是<url,HandlerMethod>
,Map中的内容如何来的呢:是在容器初始化时会建立所有url和controller的对应关系,保存到Map
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request)
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
chain.addInterceptors(getAdaptedInterceptors());
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors)
if (mappedInterceptor.matches(lookupPath, this.pathMatcher))
chain.addInterceptor(mappedInterceptor.getInterceptor());
return chain;
其中,private final List<MappedInterceptor> mappedInterceptors =
中保存的是我们项目配置文件配置的拦截器。
new ArrayList<MappedInterceptor>();
分析2:找到与用于处理request的handler相匹配的HandlerAdapter
DispatcherServlet.java
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException
for (HandlerAdapter ha : this.handlerAdapters)
if (logger.isTraceEnabled())
logger.trace("Testing handler adapter [" + ha + "]");
if (ha.supports(handler))
return ha;
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
该getHandlerAdapter函数的功能为:判断private List<HandlerAdapter> handlerAdapters;
中哪个Adapter能支持这个handler。或者说,这个handler应该与哪个Adapter进行搭档才能正常工作。
从Debug的情况来看,如果我们采用的是@RequestMapping注解形式,则获取到的是HandlerAdapter 是RequestMappingHandlerAdapter类的一个实例。
以下就是RequestMappingHandlerAdapter的support方法的实现,即只要此时的handler为HandlerMethod的实例,就是支持的。
AbstractHandlerMethodAdapter.java
public final boolean supports(Object handler)
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
RequestMappingHandlerAdapter
protected boolean supportsInternal(HandlerMethod handlerMethod)
return true;
分析3:执行所有注册拦截器的preHandler方法
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;//interceptorIndex标示着拦截器数组中下标小于等于interceptorIndex的拦截器已经被成功执行。
return true;
该函数的功能为:调用所有注册拦截器的preHandle方法,如果preHandle方法的返回结果为true,则会继续执行下面的程序。如果preHandler方法返回false,则调用triggerAfterCompletion方法。该方法的代码如下:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors))
for (int i = this.interceptorIndex; i >= 0; i--)
HandlerInterceptor interceptor = interceptors[i];
try
interceptor.afterCompletion(request, response, this.handler, ex);
catch (Throwable ex2)
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
从代码中可以看到:triggerAfterCompletion方法它会调用所有已经成功执行的拦截器的afterCompletion方法,而且是反序调用的过程。
以上可以得到的结论为:
1)只有所有注册的拦截器都执行成功,即都返回true,则doDispathcer中的代码才会继续执行。
2)如果第i个拦截器返回false,则会触发triggerAfterCompletion方法来反序执行刚刚所有已经成功执行的拦截器的afterCompletion方法。并返回false从而使得doDispatcher中的代码就不会往下执行了。
看到这里,我们也就明白了拦截器的作用。
分析4:倒序执行所有注册拦截器的postHandler方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, 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);
该函数的功能:反序执行所有注册的拦截器的postHandler方法,比较简单哈。
小结
就这样我们稍微理清了下doDispatch这个方法的基本思路,有很多细节,博文中并没有关注,如果你有兴趣,可以仔细研究。
========================漂亮的分割线========================
博文的以上部分可以看作是SpringMVC用于处理Http请求的运行主线的源码分析。而没有分析初始化主线。在看了这篇博文之后(http://downpour.iteye.com/blog/1341459),感觉对SpringMVC的初始化主线又多了一份了解。
总结如下:
1、基于框架所编写的应用程序的构成三要素。
a)入口程序DispatcherServlet,在web.xml文件中定义。
b)核心配置文件,[servlet-name]-servlet.xml文件。里面定义了相关组件。
c)控制逻辑,Controller。
2、框架的具体实现。
有两条主线。
a)初始化主线 -- 负责对SpringMVC的运行要素进行初始化。
b)用于处理http请求的运行主线 -- 负责对SpringMVC中的组件进行逻辑调度完成对Http请求的处理。
这两条主线的划分是根据servlet中的init方法和service方法来的,这两个方法的运行时间和触发条件截然不同。其中,init方法在整个系统启动时运行,且只运行一次。因此,在init方法中我们往往会对整个应用程序进行初始化操作。而service方法在整个系统运行的过程中一直处于监听模式,侦听并处理所有的web请求。
SpringMVC的整个运行体系,是由DispatcherServlet、组件和容器这三者共同构成的。 既然是三个元素之间的关系表述,我们必须以两两关系的形式进行归纳:
DispatcherServlet - 容器 —— DispatcherServlet负责对容器进行初始化,初始化的依据是核心配置文件
容器 - 组件 —— 容器对组件进行全局管理
DispatcherServlet - 组件 —— DispatcherServlet对组件进行逻辑调用
2.1)初始化主线。
首先要明确3个观点。
a)初始化主线的驱动方法:init().
b) 初始化主线的执行次序(根据DispatcherServlet的继承结构而来):HttpServletBean–>FrameworkServlet–>DispatherServlet.
c) 初始化主线的操作对象:Spring容器(WebApplicatonContext)以及组件.
(配图)
2.2)用于处理http请求的运行主线。
(配图)
以上是关于SpringMVC doDispatch方法的基本思路梳理的主要内容,如果未能解决你的问题,请参考以下文章
springmvc的问题,URL分号之后的内容读不到这是为啥呢?