欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

程序员文章站 2022-06-26 11:24:10
在阅读本文前,最好先阅读以下内容(当然,如果对 Servlet 已经有所了解,则可跳过): http://www.cnblogs.com/cyhbyw/p/8682078.html http://www.cnblogs.com/cyhbyw/p/8682307.html http://www.cnb ......

在阅读本文前,最好先阅读以下内容(当然,如果对 Servlet 已经有所了解,则可跳过):

http://www.cnblogs.com/cyhbyw/p/8682078.html

http://www.cnblogs.com/cyhbyw/p/8682307.html

http://www.cnblogs.com/cyhbyw/p/8682632.html

============分隔线==========================

 

在使用 SpringMVC 进行 Web 开发时,通常在 web.xml 中配置的 Servlet  都是 org.springframework.web.servlet.DispatcherServlet,那这个 DispatcherServlet 又是如何被 Tomcat 容器(或者其它容器)启动并加载进来的呢?

带着这个问题,写了个简单Demo进行源码调试。

Demo代码地址:https://github.com/cyhbyw/springMVC_atguigu_TongGang

Demo代码工程:springMVC_DebugSourceCode

 

首先从静态代码的角度,可以看到 DispatcherServlet 类的承继结构如下图所示

  • HttpServlet 及以上部分是 Servlet 标准中提供的接口及类
  • DispatcherServlet、FrameworkServlet、HttpServletBean 三者是 SpringMVC 提供的类,且后者依次分别是前者的父类

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

 

现在开始源码调试:

首先调用了 DispatcherServlet 的构造函数,并且从堆栈信息中可以看出,这是由 Tomcat 调用的

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

接下来当然是调用父类 FrameworkServlet 的构造函数

 SpringMVC DispatcherServlet 启动和加载过程(源码调试)

构造函数完成后,调用 Servlet 生命周期的 init() 方法;

提示,此处是 HttpServletBean 中的 init() 方法重写了GenericServlet中的 init() 方法;

这就是之前说的,建议重写这个空的 init() 方法而不建议重写那个 init(ServletConfig config) 方法,看来 SpringMVC 也确实是这样做的;

接下来代码走到 Line136行,初始化容器Bean

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

 接下来代码走到 Line493 行,初始化Web应用上下文

 SpringMVC DispatcherServlet 启动和加载过程(源码调试)

 接下来代码走到 Line552 行,创建Web应用上下文

 SpringMVC DispatcherServlet 启动和加载过程(源码调试)

获取到需要创建的Bean的Class

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

直接调用 getContextClass() 方法

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

而它内置的 contextClass 其实就是 XmlWebApplicationContext

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

 XmlWebApplicationContext 的继承结构如下图所示,不用说,肯定也是 ApplicationContext 家庭中的成员

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

Line627 行就实例化了 XmlWebApplicationContext 

同时,代码会走到 Line633 行,配置并刷新Web应用上下文

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

Line655 添加了一个应用监听器;(重要,后面会取出来用到)

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

注意,这里(SourceFilteringListener类中)方法入参处的 ApplicationListener delegate = FrameworkServlet$ContextRefreshListener,且 SourceFilteringListener 类成员变量中的 GenericApplicationListener delegate = GenericApplicationListenerAdapter;同时方法入参中的 delegate 会被 GenericApplicationListenerAdapter 包装后赋值给成员变量的 delegate(有点绕,所以用了三种颜色以示区分)

可以这样来记忆或理解:

一、对于 SourceFilteringListener 来说,其成员变量 delegate 的类型是 GenericApplicationListenerAdapter 

二、对于 GenericApplicationListenerAdapter  来说,它也有个叫做 delegate 的成员变量,且这个 delegate 的类型是 FrameworkServlet$ContextRefreshListener

(虽然这两个同名叫做 delegate 的成员变量有点绕,但它们比较重要,后面会用到)

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

SourceFilteringListener 构造完成后,回到上一层方法调用处;

接下来,代码走到 Line667 行进行刷新

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

 

这个 refresh() 方法是 Spring 中非常重要的一个方法,会调用多个方法执行多个动作,包括初始化BeanFactory、容器后处理器处理、初始化MessageSource、注册监听器等动作;

refresh() 方法非常重要!!!

refresh() 方法非常重要!!!

refresh() 方法非常重要!!!

这里,暂时关心的是,它会读取我们为 SpringMVC 所编写的配置文件中的内容(如 annotation-driven & default-servlet-handler 等,这属于上一篇文章的内容,具体可参见 这里);

之后,它会调用 Line541 行的方法,完成刷新

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

经过几个方法的调用,代码走到 Line136 ,并且此处的 listener=SourceFilteringListener(通过 Line125 获取到之前添加进来的Listener,且这个 listener=SourceFilteringListener)

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

 然后调用 SourceFilteringListener 的 onApplicationEvent() 方法

 SpringMVC DispatcherServlet 启动和加载过程(源码调试)

继续调用

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

继续调用,注意当前类是 SourceFilteringListener,且这个 delegate=GenericApplicationListenerAdapter(就是之前设置进来的

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

现在来到 GenericApplicationListenerAdapter 类中,注意此处的 delegate=FrameworkServlet$ContextRefreshListener(之前设置进来的),所以,实际上会调到 ContextRefreshListener 的 onApplicationEvent() 方法

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

进而调用到 FrameworkServlet 中内部类 ContextRefreshListener  的 onApplicationEvent() 方法,而它又是直接调用到 FrameworkServlet  的 onApplicationEvent() 方法

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

这个方法会调用到 onRefresh() 方法;而 FrameworkServlet 的 onRefresh() 方法默认实现为空(让子类扩展)

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

自然,会调用到 DispatcherServlet 的 onRefresh() 方法上,而这个方法实际上调用了其它的一系列初始化方法,如 initHandlerMappings(context) & initHandlerAdapters(context),这样在容器启动的过程中,就已经初始化完成 HandlerMapping & HandlerAdapter

SpringMVC DispatcherServlet 启动和加载过程(源码调试)

至此,DispatcherServlet 中与 Servlet 生命周期相关的 constructor() & init() 方法就已经基本完成了,接下来,就是对请求的响应,这会依次调用 Servlet 的 service() 方法,不属于本文范畴啦~~~

 

简单总结起来,Tomcat 容器启动并加载 DispatcherServlet 时所做的主要工作如下:

  • 调用 DispatcherServlet 的构造器(当然也会调用父类的构造器,不过构造器默认实现为空;这个动作很短,基本上可以忽略)
  • 调用 GenericServlet 的 init() 方法,不过,这被 HttpServletBean 重写了;同时,重写的 HttpServletBean  的 init() 方法调用了 initServletBean() 方法;而 initServletBean() 方法会完成以下操作:
  1. 初始化(创建)一个 WebApplicationContext(实际上是 WebApplicationContext 类)
  2. 调用 AbstractApplicationContext 的 refresh() 方法,完成 BeanFactory创建、读取 SpringMVC 配置文件内容、处理容器后处理器、初始化MessageResource、注册监听器等工作
  3. 通过上一步中读取到的内容,初始化 HandlerMapping & HandlerAdapter 等工作
  4. ==上面3个步骤才是重要内容==

 

总的来说,DispatcherServlet 还是一个 Servlet,遵循 constructor() --> init() --> service() --> destroy() 方法的调用流程。只不过,它的这个 init() 方法确实比较复杂(这就是本文为什么会这么长的原因,不过,看到此处的读者,恭喜,您已经看完啦!)。