1.前言

SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧

本文将介绍SpringMVC的核心分发器DispatcherServlet,通过源码分析DispatcherServlet的运行过程

2.DispatcherServlet的初始化

首先打开DispatcherServlet类继承图

可以看到,DispatcherServlet继承自HttpServlet,它的本质就是一个Servlet,这就是为什么上篇需要在web.xml通过url-mapping为DispatcherServlet配置映射请求的原因

我们从HttpServletBean开始看,HttpServletBean重写了其父类GenericServlet的init方法,我们来看看init到底做了什么(在启动Tomcat的时候会进入init方法)

ServletConfigPropertyValues是HttpServletBean的内部静态类,它负责取到web.xml中contextConfigLocation,并addPropertyValue(),在PropertyValues可以看到取到的值

BeanWrapper是一个实体包装类,简单地说,BeanWrapper提供分析和操作JavaBean的方案,如值的set/get方法、描述的set/get方法以及属性的可读可写性

ResourceLoader读取到servletContext和classLoader,servletContext装载了我们刚才的dispatcher-servlet.xml,classLoader找到我们的字节码文件并追踪到我们的jar包路径,还有很多属性不一一介绍,园友们可以自行打断点查看

web.xml部分代码,这就是我们读取的contextConfigLocation

<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springConfig/dispatcher-servlet.xml</param-value>
</init-param>
</servlet> <servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

回头再看,这里构造BeanWrapper,使用setPropertyValues设置PropertyValues,利用Spring依赖注入的特性初始化属性,读取web.xml的contextConfigLocation属性用于构造Spring上下文

用BeanWrapper的最大好处在于,我们不需要再在HttpServletBean中定义contextConfigLocation属性,并声明调用set/get方法,BeanWrapper已经帮我们做好了

按ctrl+alt+b,看initServletBean()到底在哪里被实现

接下来,我们看FrameworkServlet这个类,该类继承自HttpServletBean,看FrameworkServlet的initServletBean()方法

webApplicationContext是FrameworkServlet的上下文,initWebApplicationContext()方法为当前Servlet初始化上下文

initFrameworkServlet()交由FrameworkServlet子类实现,默认实现为空,该方法会在bean属性和上下文加载后被调用

我们现在看initWebApplicationContext()方法实现

获取根上下文,并初始化一个空的上下文

527行不会进入if,只有上下文实例在构造的时候注入才会调用

549行调用findWebApplicationContext()方法,这个方法用来查看该Servlet是否已经设置上下文,我们点进去看,没有得到attrName,返回null

当FrameworkServlet没有上下文实例定义时,调用createWebApplicationContext(),参数是我们在initWebApplicationContext()中得到的rootContext(根上下文),为FrameworkServlet初始化上下文,设置id,environment,configLocation等属性

560行onRefresh()是为了防止构造注入上下文的时候没有刷新,去手动刷新,在DispatcherServlet有实现

566行为当前Servlet设置上下文

web.xml中配置的ContextLoaderListener根据applicationContext.xml生成上下文

    <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springConfig/dispatcher-servlet.xml</param-value>
</context-param> <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

进入ContextLoaderListener,打开其父类ContextLoader,经常用SpringMVC开发的人应该对ClassPathResource比较熟悉,ClassPathResource经常被我们用来读取资源文件

ContextLoader162行指向了ContextLoader.properties,一个配置文件,它指向了org.springframework.web.context.support.XmlWebApplicationContext这个类

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

在XmlWebApplicationContext loadBeanDefinitions()获取到了contextConfigLocation,XmlBeanDefinitionReader使用xsd读取xml文件,不再详述,园友可以点进去看看

顺着刚才的思路,我们看看DispatcherServlet,DispatcherServlet重写了父类FrameworkServlet的onRefresh(ApplicationContext contex)方法

总结下HttpServletBean,FrameworkServlet和DispatcherServlet初始化过程

1.HttpServletBean

初始化web.xml中的参数

2.FrameworkServlet

将上下文赋予当前Servlet

3.DispatcherServlet

初始化HandlerMapping(请求映射),HandlerExceptionResolver(异常处理),ViewResolver(视图解析)等功能实现类

3.DispatcherServlet处理请求

在浏览器输入http://localhost:8080/springmvcdemo/employee,触发DispatcherServlet的processRequest方法

我不得不说下其父类FrameworkServlet的processRequest方法

previousLocaleContext获取和当前线程相关的LocaleContext

根据已有请求构造一个新的和当前线程相关的LocaleContext

previousAttributes获取和当前线程绑定的RequestAttributes

为已有请求构造新的ServletRequestAttributes,加入预绑定属性

initContextHolders让新构造的RequestAttributes和ServletRequestAttributes和当前线程绑定,加入到ThreadLocal,完成绑定

抽象方法doService由FrameworkServlet子类DispatcherServlet重写

resetContextHolders方法解除RequestAttributes,ServletRequestAttributes和当前线程的绑定

注册监听事件ServletRequestHandledEvent,在调用上下文的时候产生Event

现在我们看下DispatcherServlet的doService方法

attributesSnapshot用来保存request域中的数据,可以叫做“快照”

进入doDispatch方法

接下来我们看一看doDispatch方法,内容很多,我在这做些简述,细节部分后续会逐一分析

932行checkMultipart方法将request转化成Multipart request

936行HandlerExecutionChain获取Handler,有拦截器、Bean、BeanFactory,并对应上请求的Controller和Service等等

943行HandlerAdapter获取到各种argumentResolvers,用来解析参数,还能获取到各种returnValueHandlers,用来处理类返回值(后续会详解)

963行通过HandlerAdapter handle方法返回视图模型ModelAndView

969行给ModelAndView设置viewName

970行使用applyPostHandle方法拦给已注册的拦截器放行,我们此时并没有声明拦截器,spring给我们默认生成两个默认已注册的拦截器,如下

结束,文中难免有错误,希望园友能及时指出

3.参考

https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/htmlsingle/#mvc-servlet

SpringMVC源码阅读:核心分发器DispatcherServlet的更多相关文章

  1. SpringMVC源码阅读:拦截器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  2. SpringMVC源码阅读:视图解析器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  3. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  4. SpringMVC源码阅读:过滤器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  5. SpringMVC核心分发器DispatcherServlet分析[附带源码分析]

    目录 前言 DispatcherServlet初始化过程 DispatcherServlet处理请求过程 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不 ...

  6. SpringMVC源码阅读:异常解析器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  7. SpringMVC核心分发器DispatcherServlet分析

    本文将分析SpringMVC的核心分发器DispatcherServlet的初始化过程以及处理请求的过程,让读者了解这个入口Servlet的作用. DispatcherServlet初始化过程 在分析 ...

  8. SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

    SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...

  9. SpringMVC源码阅读:定位Controller

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...

随机推荐

  1. 拷贝构造函数——防篡改

    对于普通类型的对象来说,他们之间的复制是简单的,比如: int a = 88; int b = a; 而类和普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量. #include <io ...

  2. mysql免安装版 安装配置 (转)

    1. 下载MySQL Community Server 5.6.13 2. 解压MySQL压缩包     将以下载的MySQL压缩包解压到自定义目录下,我的解压目录是:     "D:\Pr ...

  3. day74天中间件介绍

    一. importlib settings 执行结果: 两个process_request  process_response按照注册顺序的倒叙进行执行 PROCESS_VIEW  Process_v ...

  4. day65 Django模板语言

      常用语法 只需要记两种特殊符号: {{  }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}. 变量 {{ 变量名 }} 变量名由字母数字和下划线组成. 点(.)在模板语言中有特殊的含 ...

  5. leetcode 105 106 从前序与中序遍历序列构造二叉树 从中序与后序遍历序列构造二叉树

    题目: 105 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = ...

  6. 如何实现session跨服务器共享

    Session共享有多种解决方法,常用的有四种:客户端Cookie保存.服务器间Session同步.使用集群管理Session.把Session持久化到数据库. 1.客户端Cookie保存 以cook ...

  7. ansj分词史上最详细教程

    最近的项目需要使用到分词技术.本着不重复造轮子的原则,使用了ansj_seg来进行分词.本文结合博主使用经过,教大家用最快的速度上手使用ansj分词. 1.给ansj来个硬广 项目的github地址: ...

  8. jmeter-server中启动后端口总是不断在变化

    1.首先找到这个文件打开: 2.修改两个地方如图: 第一个:server_port=xxxx 第二个:server.rmi.localport=xxxx 3.重启jmeter-server,这是在li ...

  9. Vue环境搭建及node安装过程整理

    一.nodejs的安装 Node.js安装包及源码下载地址为:https://nodejs.org/en/download/. 我们可以根据不同平台系统选择你需要的Node.js安装包.Node.js ...

  10. IIS Express 配置 Json

    在VS2013中调试D3官网的一些Sample过程中遇到了一个奇怪的问题:凡是Sample中使用的数据源是json文件时候,smaple 就无法在浏览器中正常运行.经调试后发现根本原因是IIS Exp ...