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

  • 相关阅读:
    Linux性能评测工具之一:gprof篇
    几个源码下载的网站
    linux svn代码回滚命令
    这就是阶层——你根本不知道世界有多残酷
    shell脚本中的数据传递方式
    XGBoost参数调优完全指南(附Python代码)
    机器学习(一) ---- 最优化理论基础
    Docker构建Java web应用服务
    使用Dockerfile创建支持SSH服务的镜像
    使用commit方式构建具有sshd服务的centos镜像
  • 原文地址:https://www.cnblogs.com/Java-Starter/p/10310565.html
Copyright © 2011-2022 走看看