分析 Spring MVC 是怎么处理请求的。首先分析 HttpServletBean、FrameworkServlet 和 DispatcherServlet 这三个 Servlet 的处理过程,最后分析 doDispatcher 的结构。

HttpServletBean

参与了创建工作,并没有涉及请求的处理。

FrameworkServlet

在类中的 service() 、doGet()、doPost()、doPut()、doDelete()、doOptions()、doTrace() 这些方法中可以看到都调用了一个共同的方法 processRequest() ,它是类在处理请求中最核心的方法。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取 LocaleContextHolder 中原来保存的 LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//获取当前请求的 LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//获取 RequestContextHolder 中原来保存的 RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//获取当前请求的 ServletRequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将当前请求的 LocaleContext 和 ServletRequestAttributes 设置到 LocaleContextHolder 和 RequestContextHolder
initContextHolders(request, localeContext, requestAttributes); try {
//实际处理请求的入口,这是一个模板方法,在 Dispatcher 类中才有具体实现
doService(request, response);
}catch (ServletException ex) {
failureCause = ex;
throw ex;
}catch (IOException ex) {
failureCause = ex;
throw ex;
}catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}finally {
//将 previousLocaleContext,previousAttributes 恢复到 LocaleContextHolder 和 RequestContextHolder 中
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
//删除了日志打印代码
//发布了一个 ServletRequestHandledEvent 类型的消息
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

DispatcherServlet

上一章中其实还没把该类讲清楚,在这个类中,里面的智行处理的入口方法应该是 doService 方法,方法里面调用了 doDispatch 进行具体的处理,在调用 doDispatch 方法之前 doService 做了一些事情:首先判断是不是 include 请求,如果是则对 request 的 Attribute 做个快照备份,等 doDispatcher 处理完之后(如果不是异步调用且未完成)进行还原 ,在做完快照后又对 request 设置了一些属性。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)){
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 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 {
//调用 doDispatch 方法
doDispatch(request, response);
}finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}

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 {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查是不是上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. 根据 request 找到 Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
} // Determine handler adapter for the current request.根据 Handler 找到对应的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
//处理 GET 、 HEAD 请求的 LastModified
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;
}
}
//执行相应的 Interceptor 的 preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler. HandlerAdapter 使用 Handler 处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//如果需要异步处理,直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//当 view 为空时,根据 request 设置默认 view
applyDefaultViewName(processedRequest, mv);
//执行相应 Interceptor 的 postHandler
mappedHandler.applyPostHandle(processedRequest, response, mv);
}catch (Exception ex) {
dispatchException = ex;
}catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//调用 processDispatchResult 方法处理上面处理之后的结果(包括处理异常,渲染页面,发出完成通知触发 Interceptor 的 afterCompletion)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", 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);
}
}
}
}

Handler,HandlerMapping,HandlerAdapter 三个区别:

  • Handler:处理器,对应 MVC 的 C层,也就是 Controller 层,具体表现形式有很多种,可以是类,方法,它的类型是 Object,只要可以处理实际请求就可以是 Handler。

  • HandlerMapping:用来查找 Handler 的。

  • HandlerAdapter :Handler 适配器,

另外 View 和 ViewResolver 的原理与 Handler 和 HandlerMapping 的原理类似。

04.SpringMVC之用的更多相关文章

  1. 05 SpringMVC:02.参数绑定及自定义类型转换&&04.SpringMVC返回值类型及响应数据类型&&05.文件上传&&06.异常处理及拦截器

    springMVC共三天 第一天: 01.SpringMVC概述及入门案例 02.参数绑定及自定义类型转换 03.SpringMVC常用注解 第二天: 04.SpringMVC返回值类型及响应数据类型 ...

  2. SpringMVC札集(04)——SpringMVC传递参数

    自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onL ...

  3. SpringMVC 04: SpringMVC中4种页面跳转方式

    转发和重定向的页面跳转方式 页面跳转方式,本质上只有2种方式:转发 + 重定向 但在SpringMVC的具体实现上,转发可以细分为:普通的页面转发 + 经由action方法的页面转发 重定向可以细分为 ...

  4. SpringMVC框架简介

    1.简介 SpringMVC也叫Spring Web  mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 01.Spring mvc的优缺点 M ...

  5. 整合SSM框架必备基础—SpringMVC(上)

    01 MVC概述 在Web系统开发中一般按照视图(View).模型(Model).控制(Controller)三层设计模式进行构建,视图层负责模型数据的渲染,将数据用一定的形式展现给用户:模型层负责监 ...

  6. springmvc 项目完整示例04 整合mybatis mybatis所需要的jar包 mybatis配置文件 sql语句 mybatis应用

    百度百科: MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBat ...

  7. 第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第二天】

    淘淘商城(SpringMVC+Spring+Mybatis)  是传智播客在2015年9月份录制的,几年过去了.由于视频里课上老师敲的代码和项目笔记有些细节上存在出入,只有根据日志报错信息作出适当的调 ...

  8. 第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第一天】

    本人做过一年的MATLAB编程和简单维护过VB和C++的项目.是跟着网上获得的黑马的Java双元视频课来自学入门Java知识和常用框架的使用. 淘淘商城(SpringMVC+Spring+Mybati ...

  9. 第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第十二天】(系统架构讲解、nginx)

    https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040 ...

随机推荐

  1. 用Python分析下王小波与李银河写情书最爱用哪些词

    作家王小波其实也是我国最早期的程序员,突发奇想,王小波写情书最喜欢用哪些词呢?用Python词云分析下! 直接上代码吧,有注释很好理解.输出的图片设置的比较大,所以运行的比较慢,可以适当把图片尺寸改小 ...

  2. selenium异步爬取(selenium+Chromedriver)

    在我们进行数据爬去的过程中,我们有时候会遇到异步加载信息的情况,以豆瓣电影分来排行榜为例,当我们在查看数据的过程中,会发现网页源码中并不包含我们想要的全部数据,但是当我们在进行向下滚动的时候,数据会一 ...

  3. Beautifulsoup网页解析——爬取豆瓣排行榜分类接口

    我们在网页爬取的过程中,会通过requests成功的获取到所需要的信息,而且,在返回的网页信息中,也是通过HTML代码的形式进行展示的.HTML代码都是通过固定的标签组合来实现页面信息的展示,所以,最 ...

  4. 打造一个window桌面应用:在线聊天对话机器人

    大家好,我是辰哥~~~ 本文目标:打造一个window桌面应用:在线聊天对话机器人. 今天辰哥教大家做一个在线聊天对话机器人桌面应用,已经打包成exe可执行文件,读者可以直接拿来使用, 先上演示图 聊 ...

  5. 第二十九篇 -- UDP和TCP

    最近在写WIFI模块,所以就想明确一些TCP和UDP的区别,发现以前的理解还是有点误区.现在重新学习. 相同点 UDP协议和TCP协议都是传输层协议 TCP(Transmission Control ...

  6. elastic query match_all 数据目标超过10000条出错 Result window is too large

    起因 elastic做文本索引,match_all目标索引超过10000条时,出错 { "error": { "root_cause": [ { "t ...

  7. Python -- 使用模块中的函数

    在确定自己不会导入多个同名函数(从不同模块导入)的情况下,你可能不希望在每次调用函数的时候,都要写上模块的名字.那么,可以使用import命令的另外一种形式: >>> from ma ...

  8. windows上传本地项目Github远程仓库(另附设置git网页链接)

    一:关于Windows平台安装git以及github的注册不在详细描述,可以参考如下经验: 安装教程:https://jingyan.baidu.com/article/925f8cb8a8e91cc ...

  9. 深入刨析tomcat 之---第4篇 tomcat4.0连接池 实现原理

    writedby 张艳涛

  10. ecshop 连接sae数据库的配置

    // database host$db_host   = "w.rdc.sae.sina.com.cn:3307";// database name$db_name   = &qu ...