zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第三节 梯度下降法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第二章 pandas 第五节 Getting Started with pandas
    Oracle数据库安装和授权
    c# 如何获取JSON文件以及如何获取Config文件(framework 和 net .Core)
    C#Core查询数据库存储EXCEL文件
    如何在WINDOW系统下编译P12证书制作
  • 原文地址:https://www.cnblogs.com/Java-Starter/p/10310565.html
Copyright © 2011-2022 走看看