6、SpringMVC源码分析(1):分析DispatcherServlet.doDispatch方法,了解总体流程
所有的http请求都会交给DispatcherServlet类的doDispatch方法进行处理,将DispatcherServlet.doDispatch函数的javadoc复制到下面:
/*
* Process the actual dispatching to the handler.
*
* The handler will be obtained by applying the servlet's HandlerMappings in
* order.The HandlerAdapter will be obtained by querying the servlet's
* installed HandlerAdapters to find the first that supports the handler
* class.
*
* All HTTP methods are handled by this method. It's up to HandlerAdapters
* or handlers themselves to decide which methods are acceptable.
*/
void org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception;
下面分析doDispatch方法的流程,采用注释源码的方式:
protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) throws Exception { // processedRequest是经过checkMultipart方法处理过的request请求
HttpServletRequest processedRequest = request;
/**
* Handler execution chain, consisting of handler object and any handler
* interceptors. Returned by HandlerMapping's HandlerMapping.getHandler
* method. 看看HandlerExecutionChain类的属性就很清楚了:
*
public class HandlerExecutionChain { private final Object handler; //这个就是和该请求对应的handler处理方法 //里面记录了所有的(any handler interceptors)和该请求相关的拦截器
private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1; //...
}
*
*/
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
ModelAndView mv = null;
Exception dispatchException = null; try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // Determine handler for the current request.Return a handler
// and any interceptors for this request.
/*
* 得到的mappedHandler包含一个请求的handler处理方法以及与该请求相关的所有拦截器
*
* DispatcherServlet.getHandler方法会在底层调用HandlerMapping.getHandler方法
* ,这个方法中会遍 历DispatcherServlet中的private List<HandlerMapping>
* handlerMappings链表,找到能够处理当前 request请求的第一个HandlerMapping实例并返回:
*
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
*
*/
mappedHandler = getHandler(processedRequest);
// 如果没有找到和该请求相对应的mappedHandler,那么就会直接返回,并应答noHandlerFound异常
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
} // Determine handler adapter for the current request.
/*
* HandlerAdapter: 它是一个接口public interface HandlerAdapter
* 看看源码上的说明:The DispatcherServlet accesses all installed
* handlers through this interface, meaning that it does not
* contain code specific to any handler type.
*
* 从后面的源码看出,在使用@RequestMapping注解标注handler方法的时候,获取到的是HandlerAdapter的
* RequestMappingHandlerAdapter实现类的一个对象。
*
* 可以看看DispatcherServlet.getHandlerAdapter方法的定义,这个对理解上回很有帮助,我们会发现
* ,getHandlerAdapter 方法和上面提到的getHandler方法一样都是寻找第一个可用的作为返回结果:
*
*
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//this.handlerAdapters的定义是 private List<HandlerAdapter> handlerAdapters
for (HandlerAdapter ha : this.handlerAdapters) {
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");
}
*
*/
HandlerAdapter ha = getHandlerAdapter(mappedHandler
.getHandler()); // Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request,
mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for ["
+ getRequestUri(request) + "] is: "
+ lastModified);
}
if (new ServletWebRequest(request, response)
.checkNotModified(lastModified) && isGet) {
return;
}
} // Apply preHandle methods of registered interceptors.
/*
* 会调用所有注册拦截器的preHandle方法,如果preHandle方法的返回结果为true,则会继续执行下面的程序,
* 否则会直接返回。
*
* 分析一下HandlerExecutionChain.applyPreHandle方法的源码 :
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//从上面的HandlerExecutionChain定义处可以看见有个interceptors,还有一个interceptorList。不知道有什么区别??!
HandlerInterceptor[] interceptors = getInterceptors();
//如果已经注册有拦截器,则遍历拦截器
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
//如果注册拦截器的preHandle方法返回一个false,则该applyPreHandle方法就会返回false,从而在doDispatcher中的代码就不会往下执行了
if (!interceptor.preHandle(request, response, this.handler)) { //这个方法要注意,它会调用所有已经成功执行的拦截器的afterCompletion方法,而且是反序调用的过程,可以分析triggerAfterCompletion
//的源代码,主要是利用interceptorIndex反减的方式实现的。下面是源码的英文注释:
//Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
//Will just invoke afterCompletion for all interceptors whose preHandle invocation
//has successfully completed and returned true.
triggerAfterCompletion(request, response, null);
return false;
}
//没成功执行一个拦截器的preHandle方法,其interceptorIndex就会增加1;原始值为-1。
this.interceptorIndex = i;
}
}
return true;
}
*
*
* 顺带看看triggerAfterCompletion的源代码,很容易理解为什么拦截器的afterCompletion方法是反序执行的:
* 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);
}
}
}
}
*
*
*/
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} // Actually invoke the handler.
/*
* 在这个函数里面会真正的执行request请求相对于的handler方法,可以想象:在真正调用方法之前还会有很多的
* 先前处理。在这里仅仅是分析出大概的代码执行流程,其细节的部分在后面的单独模块源码分析的时候做详细的讲解。
* 上面讲解到HandlerAdapter是一个接口:public interface HandlerAdapter,那么必然会有很多
* 中实现类,在采用注解@RequstMapping的方式标注handler的情况下,ha.handle方法会在底层调用具体的
* HandlerAdapter类实现方法RequestMappingHandlerAdapter.handleInternal
*
* 分析一下RequestMappingHandlerAdapter.handleInternal的源代码:
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//好像是看control的类定义处是否使用了@SessionAttributes注解,checkAndPrepare方法有什么作用???
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
} // Execute invokeHandlerMethod in synchronized block if required.
// 这里是个值得注意的地方,synchronizeOnSession的值默认为false,如果通过某个方法使得其为true,那么request对应的handler
// 将会被放在同步快中进行处理。在什么时机下,使用什么方法才能将其设置为true呢???
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
// 将handler放在同步块中处理
synchronized (mutex) {
return invokeHandleMethod(request, response, handlerMethod);
}
}
}
//在invokeHandleMethod中会①将所有标注有@ModelAttrib的方法都执行一遍,②调用invokeAndHandle(webRequest, mavContainer)
//方法,在这里面调用handler方法,③最后调用getModelAndView(mavContainer, modelFactory, webRequest)方法的到ModelAndView。
//invokeHandleMethod这个方法还有很多东西要分析,留在后面。
//从上面的③我们可以看出,无论handler采用哪种模型化处理方式,最后都是将结果转化为ModelAndView
return invokeHandleMethod(request, response, handlerMethod);
}
*/
mv = ha.handle(processedRequest, response,
mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(request, mv);
/*
* 调用request相关的拦截器的postHandle方法,注意,这个也是反序调用的。看看源代码:
*
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//注意,这里也是反序执行,而且是所有成功执行了的postHandle拦截器
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
//这里传入的参数中有mv,也就是说,我们是有办法在拦截器的postHandle方法中修改已经返回的mv
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
*/
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler,
mv, dispatchException);
} catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
ex);
} catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response,
mappedHandler, err);
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(
processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
看完源代码就可以总结出doDispath方法中处理http请求的流程了:

6、SpringMVC源码分析(1):分析DispatcherServlet.doDispatch方法,了解总体流程的更多相关文章
- SpringMVC源码情操陶冶-DispatcherServlet
本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ...
- SpringMVC源码情操陶冶-DispatcherServlet简析(二)
承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...
- springMVC源码分析--DispatcherServlet请求获取及处理
在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...
- springMVC源码分析--容器初始化(二)DispatcherServlet
在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...
- SpringMVC源码分析(3)DispatcherServlet的请求处理流程
<springmvc源码分析(2)dispatcherservlet的初始化>初始化DispatcherServlet的多个组件. 本文继续分析DispatcherServlet解析请求的 ...
- SpringMVC源码分析系列
说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而springmvc是依托着Servlet容器元素来设计的,同时sp ...
- springMVC源码分析之拦截器
一个东西用久了,自然就会从仅使用的层面上升到探究其原理的层面,在javaweb中springmvc更是如此,越是优秀的框架,其底层实现代码更是复杂,而在我看来,一个优秀程序猿就相当于一名武林高手,不断 ...
- 8、SpringMVC源码分析(3):分析ModelAndView的形成过程
首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...
- 7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解
从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, res ...
随机推荐
- 在各OJ上的名号
POJ MekakuCityActors 牛客 MekakuCityActors hdoj MekakuCityActors 这几个难度较大,所以用Actors 博客 MekakuCityActor ...
- oracle nodejs 访问
1. 下载node-oracle网址如下: https://github.com/joeferner/node-oracle 2. 测试代码: var oracle = require('oracle ...
- 调用飞信HTTP接口给自己发短信
注: 1.下文中所有HTTP请求所指的Host都是f.10086.cn 2.目前只有中国移动用户可以使用 1.打开登录页面:GET /huc/user/space/login.do?m=submit& ...
- zz 史上最全--各银行借记卡的年费、小额管理费、转账费等!
史上最全--各银行借记卡的年费.小额管理费.转账费等! 发布时间:2015-01-14 17:28:10 还在迷茫借记卡自费的菜主儿们~菜菜特别整理关于各银行借记卡.存折账户等的年费.小额管理费.转账 ...
- 理解js事件循环(event loop)
队列:先进先出 栈:后进先出 javascript的Event Loop 和 Node.js的Event Loop 区别: js(运行在浏览器),有主线程.异步任务队列的概念: node.js使用li ...
- 设置新时间校正服务器NTP SERVER
时间校正服务器IP : 10.*.*.* 适用系统:windows server 2008/windows 7 net stop w32time net start w32time w32tm /qu ...
- 洛谷2530(codevs2098)化工厂装箱员
题目:https://www.luogu.org/problemnew/show/P2530 dp或搜索. dp做法就是 当前值+1 转移到 当前某一维为0.位置前进了c位 的地方.但没写. 写了搜索 ...
- POJ3208魔鬼数
题目:http://poj.org/problem?id=3208 与一般的数位dp有点不同的是,没有给出上界,而是要通过值来判断这一位该填什么. 当然是从高位向低位填. 为了知道这一位填下去对答案有 ...
- Linux(Centos7)下安装 zookeeper docker版 集群
为了省去麻烦的软件安装,现在开发环境需要的软件越来越习惯于docker安装了,先看下安装后的截图,开发环境正在启动的容器 1.首先系统需要先支持docker …… 由于之前安装几次都没有做流程记录,在 ...
- Unigui的Grid添加汇总栏