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 ...
随机推荐
- 转-Hive/Phoenix + Druid + JdbcTemplate 在 Spring Boot 下的整合
Hive/Phoenix + Druid + JdbcTemplate 在 Spring Boot 下的整合 http://blog.csdn.net/balabalayi/article/detai ...
- MVC思想-程序的控制流程-Struts2和SpringMVC黑马流程图
1.初探 javaEE就是搞清前后台是怎么交互的,而控制那个交互的就被称为是:C:控制器 C负责协调调度程序如何执行的,M负责读数据的处理,比如说:验证输入的密码是否正确,是否 有这个权限.V就简单了 ...
- oracel SQL多表查询优化
SQL优化 1.执行路径:ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用:我们发现,单表数据的统计比多表统计的速度完全是两个概念.单表统计可能只要0.02秒,但是2张表联合统计就 ...
- Effective C++:条款23:宁以non-member、non-friend替换member函数
(一) 有个class来表示网页浏览器: class WebBrowser { public: void clearChache(); void clearHistory(); void remove ...
- hadoop之 参数调优
一. hdfs-site.xml 配置文件 1. dfs.blocksize 参数:hadoop文件块大小描述:新文件的默认块大小,以字节为单位,默认 134217728 字节.可以使用以下后缀(大小 ...
- BASIC-12_蓝桥杯_十六进制转八进制
总结: 1.使用库函数可有效节省空间,但时间花费较多; 2.由于本题的输入数据较大,又限制时间,故要注意利用空间换时间; 3.使用顺序结构换取最小运行时间; 示例代码: #include <st ...
- 【Spring-AOP-学习笔记-5】@AfterReturning增强处理简单示例
项目结构 业务代码 @Component("hello") public class HelloImpl implements Hello { // 定义一个简单方法,模拟 ...
- 小峰servlet/jsp(4)EL表达式
一.EL表达式内置对象: 二.EL表达式访问4种范围属性: 寻找值的顺序: page-->request-->session-->application; 三.EL表达式接收请求参数 ...
- mongo获取lbs数据
进入mongo目录执行./mongo 命令 #切换数据库use coachloc db.runCommand({geoNear : "coachloc" ,near : [113. ...
- 阿里云EC2+QEMU虚拟机+ROS完全教程!
---恢复内容开始--- 1.安装centos6.5 x64 同时记录,当前centos分配得到的IP,子网掩码,网关,以及MAC!!! 查看IP.mac命令ip add 查看网关命令cat /etc ...