SpringMVC源码阅读:核心分发器DispatcherServlet
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的更多相关文章
- SpringMVC源码阅读:拦截器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:视图解析器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读系列汇总
1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...
- SpringMVC源码阅读:过滤器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC核心分发器DispatcherServlet分析[附带源码分析]
目录 前言 DispatcherServlet初始化过程 DispatcherServlet处理请求过程 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不 ...
- SpringMVC源码阅读:异常解析器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC核心分发器DispatcherServlet分析
本文将分析SpringMVC的核心分发器DispatcherServlet的初始化过程以及处理请求的过程,让读者了解这个入口Servlet的作用. DispatcherServlet初始化过程 在分析 ...
- SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现
SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...
- SpringMVC源码阅读:定位Controller
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...
随机推荐
- 【转】【译】【Win10】在你的程序标题栏中显示后退按钮
原文地址:http://www.sharpgis.net/post/2015/05/21/Displaying-a-backbutton-in-your-app-window 免责声明:这篇文章基于 ...
- (原创)PetaPoco使用小记(2014-5-5更新)
接触PetaPoco已经有一段时间了,为了全面了解一下PetaPoco,刚好结合目前在做的一个项目,对常用的几个业务操作用PetaPoco进行改写,如增删改查.分页以及存储过程的调用,在文章的最后附上 ...
- 在AbpZero中hangfire后台作业的使用——hangfire的调度
在abpzero框架中,hangfiire通过依赖注入来进行接口的调用 hangfire的事件处理分为以下几种: 1.基于队列的任务处理(Fire-and-forget jobs) var jobId ...
- LoadRunner 12下载和安装教程
我们利用LoadRunner可以对Web应用系统进行性能压力测试,本篇博客将和大家介绍下LoadRunner 12的下载和安装,在后续的博客中将和大家介绍其使用的方法. 1.LoadRunner 12 ...
- 创建/删除Cookie数据
//1.编写(创建 和 修改 一样) HttpCookie cookie = new HttpCookie("userName");cookie.Value = "顾志海 ...
- FFmpeg命令详解
命令格式 功能 FFmpeg命令是在ffmpeg.exe可执行文件环境下执行,ffmpeg.exe用于音视频的转码,加水印,去水印,视频剪切,提取音频,提取视频,码率控制等等功能. 最简单的命令 ff ...
- Sentinel 哨兵 实现redis高可用
本文链接:http://www.cnblogs.com/zhenghongxin/p/8885879.html 我们知道redis是有主从复制的,例如下图: 但如果master主进程挂掉之后,没有sl ...
- Android安全防护防护———加密算法
摘要 这篇文章本来早就应该写了,但是由于项目一直开发新的需求,就拖后了.现在有时间了,必须得写了.现在Android应用程序对安全防范这方面要求越来越高了.特别是金融行业,如果金融app没有没有做好相 ...
- mysql优化器在统计全表扫描的代价时的方法
innodb 的聚集索引 的叶子结点 存放的 是 索引值以及数据页的偏移量 那么在计算全表扫描的代价是怎么计算的呢? 我们知道代价 为 cpu代价+io代价 cpu代价 就是 每5条记录比对 计算一个 ...
- 验证resneXt,densenet,mobilenet和SENet的特色结构
简介 图像分类对网络结构的要求,一个是精度,另一个是速度.这两个需求推动了网络结构的发展. resneXt:分组卷积,降低了网络参数个数. densenet:密集的跳连接. mobilenet:标准卷 ...