前言

最近打算花点时间好好看看spring的源码,然而现在Spring的源码经过迭代的版本太多了,比较庞大,看起来比较累,所以准备从最初的版本(interface21)开始入手,仅用于学习,理解其设计思想,后续慢慢研究其每次版本变更的内容。。。

先从interface21的一个典型web工程例子看起,宠物诊所 - petclinic,因为该工程基本涵盖了Spring的APO、IOC、JDBC、Web MVC、事务、国际化、主题切换、参数校验等主要功能。。。

继上一篇,了解完ContextLoaderListener(加载Spring Web Application Context)的流程后,看看Spring mvc的关键控制器 - DispatcherServlet初始化流程是如何的~~~~~~~

对应的web.xml配置

    <servlet>
<servlet-name>petclinic</servlet-name>
<servlet-class>com.interface21.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>petclinic</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

这里有一个地方需要注意下,load-on-startup的配置,关于该Servlet参数的含义,可以先看下web-app_2_3.dtd文件中的解释,如下,大致意思就是,如果没配置load-on-startup或该值为负数,则servlet容器可以自由选择什么时候加载该servlet(实例化并执行Servlet的init方法),如果为0或正数,则必须在容器启动时加载Servlet并执行其init方法,且数值越小的Servlet优先加载,回过头看我们的DispatcherServlet,由于load-on-startup配置成1,所以会在启动的时候,执行init方法,即本文要关注的DispatcherServlet初始化流程:

<!--
The load-on-startup element indicates that this servlet should be
loaded (instantiated and have its init() called) on the startup
of the web application. The optional contents of
these element must be an integer indicating the order in which
the servlet should be loaded. If the value is a negative integer,
or the element is not present, the container is free to load the
servlet whenever it chooses. If the value is a positive integer
or 0, the container must load and initialize the servlet as the
application is deployed. The container must guarantee that
servlets marked with lower integers are loaded before servlets
marked with higher integers. The container may choose the order
of loading of servlets with the same load-on-start-up value. Used in: servlet
-->

执行时序图(看不清的话可以点击查看原图)

时序图中的各个步骤简要分析

执行的入口在HttpServletBean类的init方法,由于DispatcherServlet的load-on-startup参数配置成1,所以在Servlet容器(tomcat)启动时,会自动调用该Servlet的init方法。

步骤描述:

  1. 首先,执行DispatcherServlet的父类HttpServletBean的init方法;

    public final void init() throws ServletException {
    this.identifier = "Servlet with name '" + getServletConfig().getServletName() + "' "; logger.info(getIdentifier() + "entering init..."); // Set bean properties
    try {
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), requiredProperties);
    BeanWrapper bw = new BeanWrapperImpl(this);
    bw.setPropertyValues(pvs);
    logger.debug(getIdentifier() + "properties bound OK"); // Let subclasses do whatever initialization they like
    initServletBean();
    logger.info(getIdentifier() + "configured successfully");
    } catch (BeansException ex) {
    String mesg = getIdentifier() + ": error setting properties from ServletConfig";
    logger.error(mesg, ex);
    throw new ServletException(mesg, ex);
    } catch (Throwable t) {
    // Let subclasses throw unchecked exceptions
    String mesg = getIdentifier() + ": initialization error";
    logger.error(mesg, t);
    throw new ServletException(mesg, t);
    }
    }
  2. 获取Servlet的初始化参数,创建BeanWrapperImpl 实例,设置属性值;
  3. 执行HttpServletBean的子类FrameworkServlet的initServletBean方法;
        protected final void initServletBean() throws ServletException {
    long startTime = System.currentTimeMillis();
    logger.info("Framework servlet '" + getServletName() + "' init");
    this.webApplicationContext = createWebApplicationContext();
    initFrameworkServlet();
    long elapsedTime = System.currentTimeMillis() - startTime;
    logger.info("Framework servlet '" + getServletName() + "' init completed in " + elapsedTime + " ms");
    }
  4. 调用FrameworkServlet的createWebApplicationContext方法。
        private WebApplicationContext createWebApplicationContext() throws ServletException {
    getServletContext().log("Loading WebApplicationContext for servlet '" + getServletName() + "'");
    ServletContext sc = getServletConfig().getServletContext();
    WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(sc);
    String namespace = getNamespace(); WebApplicationContext waca = (this.contextClass != null) ?
    instantiateCustomWebApplicationContext(this.contextClass, parent, namespace) :
    new XmlWebApplicationContext(parent, namespace);
    logger.info("Loading WebApplicationContext for servlet '" + getServletName() + "': using context class '" + waca.getClass().getName() + "'");
    waca.setServletContext(sc); if (this.publishContext) {
    // Publish the context as a servlet context attribute
    String attName = getServletContextAttributeName();
    sc.setAttribute(attName, waca);
    logger.info("Bound context of servlet '" + getServletName() + "' in global ServletContext with name '" + attName + "'");
    }
    return waca;
    }
  5. 进入createWebApplicationContext方法,从ServletContext的属性中获取WebApplicationContext,该上下文是由ContextLoaderListener加载的
  6. 创建子上下文XmlWebApplicationContext,其父上下文为之前ContextLoaderListener加载的WebApplicationContext,关于这两个上下文的关系,及负责加载的内容,可参考这张图,图片来源(http://jinnianshilongnian.iteye.com/blog/1602617/
  7. 执行子上下文XmlWebApplicationContext的waca.setServletContext方法,去加载petclinic-servlet.xml配置文件(国际化,主题,HandlerMapping、HandlerAdapter、视图解析等相关配置),关于WebApplicationContext上下文的加载流程,可参考上一篇(Spring Web Application Context加载流程),流程都是相同的;
  8. 将该子上下文设置到ServletContext属性中
  9. 进入DispatcherServlet类的initFrameworkServlet方法,主要执行一些初始化工作
        protected void initFrameworkServlet() throws ServletException {
    initLocaleResolver();
    initThemeResolver();
    initHandlerMappings();
    initHandlerAdapters();
    initViewResolver();
    }
  10. 国际化相关:执行initLocaleResolver方法,从上下文中获取localeResolver bean,没有则使用默认AcceptHeaderLocaleResolver
        private void initLocaleResolver() throws ServletException {
    try {
    this.localeResolver = (LocaleResolver) getWebApplicationContext().getBean(LOCALE_RESOLVER_BEAN_NAME);
    logger.info("Loaded locale resolver [" + this.localeResolver + "]");
    } catch (NoSuchBeanDefinitionException ex) {
    // We need to use the default
    this.localeResolver = new AcceptHeaderLocaleResolver();
    logger.info("Unable to locate locale resolver with name '" + LOCALE_RESOLVER_BEAN_NAME + "': using default [" + this.localeResolver + "]");
    } catch (BeansException ex) {
    // We tried and failed to load the LocaleResolver specified by a bean
    throw new ServletException("Fatal error loading locale resolver with name '" + LOCALE_RESOLVER_BEAN_NAME + "': using default", ex);
    }
    }
  11. 主题相关:执行initThemeResolver方法,从上下文中获取themeResolver bean,没有则使用默认FixedThemeResolver
        private void initThemeResolver() throws ServletException {
    try {
    this.themeResolver = (ThemeResolver) getWebApplicationContext().getBean(THEME_RESOLVER_BEAN_NAME);
    logger.info("Loaded theme resolver [" + this.themeResolver + "]");
    } catch (NoSuchBeanDefinitionException ex) {
    // We need to use the default
    this.themeResolver = new FixedThemeResolver();
    logger.info("Unable to locate theme resolver with name '" + THEME_RESOLVER_BEAN_NAME + "': using default [" + this.themeResolver + "]");
    } catch (BeansException ex) {
    // We tried and failed to load the ThemeResolver specified by a bean
    throw new ServletException("Fatal error loading theme resolver with name '" + THEME_RESOLVER_BEAN_NAME + "': using default", ex);
    }
    }
  12. 执行initHandlerMappings方法,从上下文中获取HandlerMapping类型的bean,没有则使用默认BeanNameUrlHandlerMapping
        private void initHandlerMappings() throws ServletException {
    this.handlerMappings = new ArrayList(); // Find all HandlerMappings in the ApplicationContext
    String[] hms = getWebApplicationContext().getBeanDefinitionNames(HandlerMapping.class);
    for (int i = 0; i < hms.length; i++) {
    initHandlerMapping(hms[i]);
    logger.info("Loaded handler mapping [" + hms[i] + "]");
    } // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    if (this.handlerMappings.isEmpty()) {
    initDefaultHandlerMapping();
    logger.info("No HandlerMappings found in servlet '" + getServletName() + "': using default");
    } else {
    // We keep HandlerMappings in sorted order
    Collections.sort(this.handlerMappings, new OrderComparator());
    }
    }
        private void initDefaultHandlerMapping() throws ServletException {
    try {
    HandlerMapping hm = new BeanNameUrlHandlerMapping();
    hm.setApplicationContext(getWebApplicationContext());
    this.handlerMappings.add(hm);
    } catch (ApplicationContextException ex) {
    throw new ServletException("Error initializing default HandlerMapping: " + ex.getMessage(), ex);
    }
    }
  13. 执行initHandlerAdapters方法,从上下文中获取HandlerAdapter类型的bean,没有则使用默认SimpleControllerHandlerAdapter
        private void initHandlerAdapters() throws ServletException {
    this.handlerAdapters = new ArrayList(); String[] has = getWebApplicationContext().getBeanDefinitionNames(HandlerAdapter.class);
    for (int i = 0; i < has.length; i++) {
    initHandlerAdapter(has[i]);
    logger.info("Loaded handler adapter [" + has[i] + "]");
    } // Ensure we have at least one HandlerAdapter, by registering
    // a default HandlerAdapter if no other adapters are found.
    if (this.handlerAdapters.isEmpty()) {
    initDefaultHandlerAdapter();
    logger.info("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
    } else {
    // We keep HandlerAdapters in sorted order
    Collections.sort(this.handlerAdapters, new OrderComparator());
    }
    }
        private void initDefaultHandlerAdapter() throws ServletException {
    try {
    HandlerAdapter ha = new SimpleControllerHandlerAdapter();
    ha.setApplicationContext(getWebApplicationContext());
    this.handlerAdapters.add(ha);
    } catch (ApplicationContextException ex) {
    throw new ServletException("Error initializing default HandlerAdapter: " + ex.getMessage(), ex);
    }
    }
  14. 执行initViewResolver方法,从上下文中获取viewResolver bean,没有则使用默认InternalResourceViewResolver
        private void initViewResolver() throws ServletException {
    try {
    this.viewResolver = (ViewResolver) getWebApplicationContext().getBean(VIEW_RESOLVER_BEAN_NAME);
    logger.info("Loaded view resolver [" + viewResolver + "]");
    } catch (NoSuchBeanDefinitionException ex) {
    // We need to use the default
    this.viewResolver = new InternalResourceViewResolver();
    try {
    this.viewResolver.setApplicationContext(getWebApplicationContext());
    } catch (ApplicationContextException ex2) {
    throw new ServletException("Fatal error initializing default ViewResolver");
    }
    logger.info("Unable to locate view resolver with name '" + VIEW_RESOLVER_BEAN_NAME + "': using default [" + this.viewResolver + "]");
    } catch (BeansException ex) {
    // We tried and failed to load the ViewResolver specified by a bean
    throw new ServletException("Fatal error loading view resolver: bean with name '" + VIEW_RESOLVER_BEAN_NAME + "' is required in servlet '" + getServletName() + "': using default", ex);
    }
    }
  15. 返回到FrameworkServlet类
  16. 返回到HttpServletBean类
  17. Servlet的init方法执行完毕

另外可以关注下该Servlet的销毁方法,类似的,也是执行一些资源销毁等操作,销毁工厂创建的单例bean对象,发布ContextClosedEvent事件等;

    public void destroy() {
getServletContext().log("Closing WebApplicationContext for servlet '" + getServletName() + "'");
getWebApplicationContext().close();
}
    public void close() {
logger.info("Closing application context [" + getDisplayName() + "]"); // destroy all cached singletons in this context,
// invoking DisposableBean.destroy and/or "destroy-method"
getBeanFactory().destroySingletons(); // publish respective event
publishEvent(new ContextClosedEvent(this));
}

interface21代码参考

https://github.com/peterchenhdu/interface21

interface21 - web - DispatcherServlet(DispatcherServlet初始化流程)的更多相关文章

  1. SpringMVC源码剖析(三)- DispatcherServlet的初始化流程

    在我们第一次学Servlet编程,学Java Web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根据需要重写一下doGet,doPost方法,跳转 ...

  2. spring自动扫描、DispatcherServlet初始化流程、spring控制器Controller 过程剖析

    spring自动扫描1.自动扫描解析器ComponentScanBeanDefinitionParser,从doScan开始扫描解析指定包路径下的类注解信息并注册到工厂容器中. 2.进入后findCa ...

  3. SpringMVC深度探险(三) —— DispatcherServlet与初始化主线

    在上一篇文章中,我们给出了构成SpringMVC应用程序的三要素以及三要素的设计过程.让我们来归纳一下整个设计过程中的一些要点: SpringMVC将Http处理流程抽象为一个又一个处理单元 Spri ...

  4. 2.SpringMVC源码分析:DispatcherServlet的初始化与请求转发

    一.DispatcherServlet的初始化 在我们第一次学Servlet编程,学java web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根 ...

  5. SpringMVC源码分析3:DispatcherServlet的初始化与请求转发

    在我们第一次学Servlet编程,学java web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根据需要重写一下doGet,doPost方法,跳转 ...

  6. Spring框架系列(13) - SpringMVC实现原理之DispatcherServlet的初始化过程

    前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet ...

  7. SpringMVC源码分析(3)DispatcherServlet的请求处理流程

    <springmvc源码分析(2)dispatcherservlet的初始化>初始化DispatcherServlet的多个组件. 本文继续分析DispatcherServlet解析请求的 ...

  8. DispatcherServlet的初始化(二)

    DispatcherServlet的初始化在springmvc的启动中有讲过,这一篇在上一篇的基础上接着讲.DispatcherServlet作为springmvc的前端控制器,还需要初始化其他的模块 ...

  9. DispatcherServlet的处理流程

    前言 上一篇介绍了SpringMVC的启动过程,DispatcherServlet作为一个前端控制器,分发处理http请求 1.DispatcherServlet流程图 具体流程: 1. 用户发请求- ...

随机推荐

  1. freemaker超详细 讲解 配置

    一.FreeMarker简介 二.第一个FreeMark示例 2.1.新建一个Maven项目 2.2.添加依赖 2.3.添加存放模板的文件夹 2.4.添加模板 2.5.解析模板 2.6.运行结果 三. ...

  2. python 去除字符串末尾的子串

    建议使用 "字符串".replace("要去除的字符串", "") 尽量不要使用 strip() ,有时会引入意想不到的结果.strip() ...

  3. Blast

    NCBI 教程:https://www.ncbi.nlm.nih.gov/books/NBK279681/ 本地使用 blast 的步骤 1. 构建本地数据库索引 $makeblastdb -in h ...

  4. python datetime模块用法

    1. 创建naive(无时区信息)的datetime对象 import datetime dt_utc = datetime.datetime.utcnow() dt_utc # datetime.d ...

  5. oracle序列的使用

    第一天:序列的使用 在oracle中sequence就是所谓的序列号,每次取的时候它会自动增加,一般用在需要按序列号排序的地方.  1.Create Sequence  你首先要有CREATE SEQ ...

  6. 去掉input[type="number"]的默认样式

    input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{ -webkit-appearance: none !importa ...

  7. 05-使用jQuery操作input的value值

    表单控件是我们的重中之重,因为一旦牵扯到数据交互,离不开form表单的使用,比如用户的注册登录功能等 那么通过上节知识点我们了解到,我们在使用jquery方法操作表单控件的方法: $(selector ...

  8. Exception、Error、运行时异常与一般异常有何异同

    转自博客  https://blog.csdn.net/m0_37531231/article/details/79502778 一.开场白 对于程序运行过程中的可能出现异常情况,java语言使用一种 ...

  9. Vuejs(14)——在v-for中,利用index来对第一项添加class

    版权声明:出处http://blog.csdn.net/qq20004604 (1)在v-for中,利用index来对第一项添加class <a class="list-group-i ...

  10. Delphi fmx控件在手机滑动与单击的问题

    Delphi fmx控件在手机滑动与单击的问题 (2016-03-08 10:52:00) 转载▼ 标签: it delphi 分类: Delphi10 众所周知,fmx制作的app,对于象TEdit ...