zoukankan      html  css  js  c++  java
  • SpringMVC DispatcherServlet 启动和加载过程(源码调试)

    在阅读本文前,最好先阅读以下内容(当然,如果对 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 提供的类,且后者依次分别是前者的父类

     

    现在开始源码调试:

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

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

     

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

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

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

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

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

     

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

     

    获取到需要创建的Bean的Class

    直接调用 getContextClass() 方法

    而它内置的 contextClass 其实就是 XmlWebApplicationContext

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

    Line627 行就实例化了 XmlWebApplicationContext 

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

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

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

    可以这样来记忆或理解:

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

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

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

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

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

     

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

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

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

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

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

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

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

     然后调用 SourceFilteringListener 的 onApplicationEvent() 方法

     

    继续调用

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

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

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

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

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

    至此,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() 方法确实比较复杂(这就是本文为什么会这么长的原因,不过,看到此处的读者,恭喜,您已经看完啦!)。

  • 相关阅读:
    Linux下OpenSSL自签ssl证书
    戌蛤怒触铁铁树
    Python自动化办公-自动化操作Excell-openpyxl
    Python正则表达式
    Django中F查询
    如何知道自家的宽带是多少兆?
    ‘Diango中明明模版的名称写的是对的,但是访问url的时候发现没调用模版’解决方案
    Django中的聚合函数
    Django中的跨关系查询
    【RabbitMQ】零、Windows64位安装
  • 原文地址:https://www.cnblogs.com/cyhbyw/p/8683251.html
Copyright © 2011-2022 走看看