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 ...
随机推荐
- 前端内容安全策略(csp)
什么是CSP CSP全称Content Security Policy ,可以直接翻译为内容安全策略,说白了,就是为了页面内容安全而制定的一系列防护策略. 通过CSP所约束的的规责指定可信的内容来源( ...
- 剑指offer-青蛙变态跳台阶-全概率公式
- Vault 0.10包含了web ui
Vault 是一个很不错的访问控制,secret api key 管理工具 新的0.10 有好多新的功能的添加,最棒的是有一个web ui 了 包含的新特性如下: K/V Secrets Engine ...
- FastAdmin composer json 版本说明
来源于 FastAdmin 执行 composer update 后将 ThinkPHP 升级到了 V5.1. FastAdmin 是基于 ThinkPHP 5.0.x 开发的,而 ThinkPHP ...
- java 方向术语
缩写 英文 中文意思 POJO Plain Ordinary Java Object 简单的Java对象 slf4j Simple Logging Facade for Java 简单日志门面,跟 C ...
- idea新用法
https://blog.csdn.net/linsongbin1/article/details/80211919
- java编译需要文件后缀名.java 而运行不需要后缀名.class
对于java源文件HelloWorld.java编译命令:javac HelloWorld.java运行命令:java HelloWorld 编译需要文件后缀名.java 而运行不需要后缀名.clas ...
- json格式字符串处理
public class InternalClass { public int MID; public string Name; ...
- 从Qt谈到C++(一):关键字explicit与隐式类型转换
转载:果冻虾仁 提出疑问 当我们新建了一个Qt的widgets应用工程时.会自动生成一个框架,包含了几个文件. 其中有个mainwindow.h的头文件.就是你要操纵的UI主界面了.我们看看其中的一段 ...
- $(function(){}) ,$(document).ready(function(){}),window.onload = function(){...},$(window).load(function(){...})区别
1. 写法: $(function(){}) ,$(document).ready(function(){})是一样的 2. 时间: window.onload和$(window).load ...