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 到底做了什么
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,FrameworkServletBean 和 DispatcherServlet 的作用
1.HttpServletBean
初始化 Web.xml 中的参数
2.FrameworkServlet
将上下文赋予当前 Servlet
3.DispatcherServlet
初始化 HandlerMapping(请求映射),HandlerExceptionResolver(异常处理),ViewResolver(视图解析) 等功能实现类
3.DispatcherServlet 处理请求
我不得不说下其父类 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://www.cnblogs.com/Java-Starter/p/10310565.html