SpringMvc请求处理流程与源码探秘
流程梳理
dispatcherServlet作为前端控制器的主要作用就是接受请求与处理响应。
不过它不是传统意义上的servlet,它在接受到请求后采用转发的方式,将具体工作交给专业人士去做。
参与角色主要有:
前端控制器(DispatcherServlet)
处理映射器(HandlerMapping)
处理适配器(HandlerAdapter)
处理器((Handler)Controller)
视图解析器(ViewReslover)
视图(View)
找了一张图,把请求过程与步骤清晰的呈现了出来
第一步:前端控制器dispatcher接受请求
Client---url--->Dispatcher
第二步:前端控制器去发起handler映射查找请求
Dispatcher---HttpServletRequest---> HandlerMapping
第三步:处理器映射器查找hanlder并返回HandlerExetuionChain
Dispatcher <---HandlerExeutionChain---HandlerMapping
第四步:前端控制器发起请求处理器适配器请求执行
Dispatcher---Handler---> HandlerAdapter
第五步:处理器适配器去调用handler执行
HandlerAdapter---HttpServletRequest> Handler(Controller)
第六步:处理器处理后返回ModelAndView给HandlerAdapter
HandlerAdapter <---ModelAndView---Handler(Controller)
第七步:处理器适配器将ModelAndView返回给前端控制器
Dispatcher <---ModelAndView---HandlerAdapter
第八步:前端控制器请求视图解析器解析ModelAndView
Dispatcher---ModelAndView---> ViewReslover
第九步:视图解析器解析视图后返回视图View给前端控制器
Dispatcher <---View---ViewReslover
第十步:前端控制器请求视图要求渲染视图
Dispatcher--->View--->render
第十一步:前端控制器返回响应
Response <---Dispatcher
源码探秘
第一步接受请求:
我们可以来看看DispatcherServlet的继承结构
其实DispatcherServlet能处理请求是因为HttpServlet类的service方法,而HttpServlet又来自Servlet接口定义的规范。
可以看到抽象类HttpServlet实现了接口Servlet的service方法,根据请求类型不同执行了不同的方法(doGet,doPost)
当请进来后,由HttpServlet的子类FrameworkServlet重写的service方法执行请求,可以看到437行子类调用了父类的service方法,然后在父类执行doGet之类的方法时,由于子类FrameworkServlet重写了父类方法,交由子类执行,所以进到了我的doGet断点里面,它调用了处理请求方法。
接下来我们看看ProcessRequest方法的源码
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = this.buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
this.initContextHolders(request, localeContext, requestAttributes); try {
this.doService(request, response);
} catch (IOException | ServletException var16) {
failureCause = var16;
throw var16;
} catch (Throwable var17) {
failureCause = var17;
throw new NestedServletException("Request processing failed", var17);
} finally {
this.resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
} this.logResult(request, response, (Throwable)failureCause, asyncManager);
this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
} }
前面一系列初始化工作我们先不管,看看重要的部分,try里面的doService方法
跟踪进去看了一下,由于它是抽象方法,所以会由子类实现和执行,也就是我们的DispatchServlet类了
老规矩,先贴上源码,它是DispatchServlet的doService方法--------------------------------------------------------------------------------------------------------------
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames(); label95:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label95;
} attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
} request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
} request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
} try {
this.doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot);
} } }
所以第一步也就完成了,第一步的任务就是走进这里来。
第二步:前端控制器去发起handler映射查找请求
Dispatcher---HttpServletRequest---> HandlerMapping
上面的源码中主要工作就是给request实例设置一系列参数,要注意的就是doDispatch方法,这里面就是mvc的核心了,前面第一张交互图里面的流程都是在这里实现的。
可以看到,通过HttpRequestServlet作为参数请求handlerMapping
第三步:处理器映射器查找hanlder并返回HandlerExetuionChain
Dispatcher <---HandlerExeutionChain---HandlerMapping
可以看到上图中返回了mappedHandler变量,就是HandlerExtuceChain类型
可以看到,已经找到并返回了我们的HomeController处理器(Hanlder)
第四步:前端控制器发起请求处理器适配器请求执行
Dispatcher---Handler---> HandlerAdapter
从508行可以看到,适配器传入handler对象和reaquest等信息,执行handler()方法
第五步:处理器适配器去调用handler执行
HandlerAdapter---HttpServletRequest> Handler(Controller)
从这里可以看到,调用的handlerInternal是个抽象方法,会调用子类的实现方法,子类由RequestMappingHandlerAdapter实现,这个类也是我们经常在xml里面配置的类
通过invokeHandlerMethod方法执行进到controller里面
方法执行后返回我们的index
第六步:处理器处理后返回ModelAndView给HandlerAdapter
HandlerAdapter <---ModelAndView---Handler(Controller)
通过调用invokeHandlerMethod方法返回ModelAndView
第七步:处理器适配器将ModelAndView返回给前端控制器
Dispatcher <---ModelAndView---HandlerAdapter
第八步:前端控制器请求视图解析器解析ModelAndView
Dispatcher---ModelAndView---> ViewReslover
第九步:视图解析器解析视图后返回视图View给前端控制器
Dispatcher <---View---ViewReslover
可以看到,返回的视图,url指向index.jsp页面
第十步:前端控制器请求视图要求渲染视图
Dispatcher--->View--->render
如果View对象不为空,将会调用render方法渲染
如果返回的是json对象,属于接口的,是不会走这里的
此时会找对应的视图解析器去渲染
里面其实也没干啥,就做了个跳转,到jsp页面去绑定数据
第十一步:前端控制器返回响应
Response <---Dispatcher
到这里也就基本上完了。
处理请求完成后做了个重置工作,然后发布一个事件,你可以选择监听这个事件,做相应处理。
再看看response里面
这个就是我们页面上的内容了。
全剧终
SpringMvc请求处理流程与源码探秘的更多相关文章
- SpringMVC执行流程及源码分析
SpringMVC流程及源码分析 前言 学了一遍SpringMVC以后,想着做一个总结,复习一下.复习写下面的总结的时候才发现,其实自己学的并不彻底.牢固.也没有学全,视频跟书本是要结合起来一起, ...
- Struts2请求处理流程及源码分析
1.1 Struts2请求处理 1. 一个请求在Struts2框架中的处理步骤: a) 客户端初始化一个指向Servlet容器的请求: b) 根据Web.xml配置,请求首先经过ActionConte ...
- springmvc工作原理以及源码分析(基于spring3.1.0)
springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...
- 可视化工具gephi源码探秘(二)---导入netbeans
在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码 ...
- 可视化工具gephi源码探秘(二)
在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码 ...
- Android 全面插件化 RePlugin 流程与源码解析
转自 Android 全面插件化 RePlugin 流程与源码解析 RePlugin,360开源的全面插件化框架,按照官网说的,其目的是“尽可能多的让模块变成插件”,并在很稳定的前提下,尽可能像开发普 ...
- SpringMVC+Maven开发项目源码详细介绍
代码地址如下:http://www.demodashi.com/demo/11638.html Spring MVC概述 Spring MVC框架是一个开源的Java平台,为开发强大的基于Java的W ...
- Spark Streaming运行流程及源码解析(一)
本系列主要描述Spark Streaming的运行流程,然后对每个流程的源码分别进行解析 之前总听同事说Spark源码有多么棒,咱也不知道,就是疯狂点头.今天也来撸一下Spark源码. 对Spark的 ...
- django Rest Framework----APIView 执行流程 APIView 源码分析
在django—CBV源码分析中,我们是分析的from django.views import View下的执行流程,这篇博客我们介绍django Rest Framework下的APIView的源码 ...
随机推荐
- Confluence 6 修改默认空间标识图片
空间标识图片在边栏上的站点目录(Sites Directory)中作为图标进行显示.默认的空间标识图片将会应用到所有的空间中,如果你没有自定义的空间标识被定义的话,请查看 Configure the ...
- 进入页面,根据后台传过来的flag 判断列表隐藏与否
需求描述:页面中有两个列表,一个已添加,一个可以添加,进入页面的时候,如果已添加中有数据则显示,没有数据就隐藏,emmmm,这种需求,我该怎么吐槽嗷嗷嗷 解决思路:让已添加的列表的div默认隐藏,前台 ...
- Java 并发类
java.util.concurrent包里 提供了一批线程安全的类 一. java.util.concurrent.atomic java.util.concurrent.atomic包里的原子处理 ...
- Metasploit one test
1.对Metasploit的文件结构层次做一个目录结构图 2.漏洞利用的原理及其过程 攻击者发送一个附加攻击载荷的漏洞攻击代码给存在漏洞的系统.漏洞攻击代码首先执行,如果执行成功,攻击载荷中的实际代码 ...
- Java接口自动化测试之HTTPClient学习(四)
pom.xml 文件中dependency <dependencies> <dependency> <groupId>org.testng</groupId ...
- 微信JSAPI分享朋友圈调试经验:invalid signature签名错误排查
.invalid signature签名错误.建议按如下顺序检查: 1.确认签名算法正确,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapi ...
- Eclipse中如何打开Map/Reduce Locations窗口
Window->Show View->Other->MapReduce Tools,双击打开
- 小甲鱼python基础教程飞机大战源码及素材
百度了半天小甲鱼python飞机大战的源码和素材,搜出一堆不知道是什么玩意儿的玩意儿. 最终还是自己对着视频一行行代码敲出来. 需要的同学点下面的链接自取. 下载
- snmp v3的安全配置 snmp认证与加密配置(53)
http://www.ttlsa.com/zabbix/snmp-v3-configuration/
- C# 多线程下 静态类字段异常