SpringMVC的启动过程

SpringMVC的启动过程

前言

下面是一个SpringMVC应用的配置文件,需要注意两个地方,一个是ContextLoaderListener,一个是dispatcherServlet。web容器正是通过这两个配置才和spring管理起来。ContextLoaderListener与web容器的ServletContext关联,为Spring的IOC容器提供了一个宿主环境。在建立起IOC容器体系之后,把DispatcherServlet作为SpringMVC处理web请求的转发器建立起来,完成响应http请求的准备。

SpringMVC启动过程大致分为两个阶段:

第一阶段.ContextLoaderListener初始化,实例化IOC容器,并将此容器注册到ServletContext中。

第二阶段DispatcherServlet初始化,建立自己的上下文,也注册到ServletContext中。

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
  http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1">

<display-name>ota</display-name>
<description>ota web application</description>

<!-- 系统组件加载顺序:context-param -> listener -> filter -> servlet -->
 <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- GZip -->
<filter>
    <filter-name>gzipFilter</filter-name>
    <filter-class>com.travelsky.ibeplus.compress.Compress2WayFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>gzipFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!--  Servlet that dispatches request to registered handlers (Controller implementations).  -->
<servlet>
    <servlet-name>ota</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/mvc-core-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- 需要定义在对应的servlet之后 -->
<servlet-mapping>
    <servlet-name>ota</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

</web-app> 

1、Spring IOC容器的启动

Spring IOC容器的启动在我博客中这一篇博文“Spring是怎样启动的” 有详细介绍,这里再简单概述下。

ContextLoaderListener实现ServletContextListener,这个接口里面的方法会结合web容器的生命周期被调用。因为ServletContextListener是ServletContext的监听者,如果ServletContext发生变化,会触发相应的事件,而监听者一直对这些事件进行监听,如果接受到了监听的事件,就会作于相应处理。例如在服务器启动,ServletContext被创建的时候,ServletContextListener的contextInitialized()方法被调用,从而拉开了初始化Spring IOC容器的序幕

2、DispatcherServlet的启动 (HttpServletBean * FrameworkServlet * DispatcherServlet)

DispatchServlet本质上是一个Servlet,web容器启动的时候,servlet也会初始化,其init方法被调用,开启初始化之旅。

DispatchServlet会建立自己的上下文来持有Spring MVC特殊的bean对象,在建立这个自己持有的Ioc容器的时候,会从ServletContext中得到根上下文作为DispatchServlet上下文的parent上下文。有了这个根上下文,再对自己持有的上下文进行初始化,最后把自己持有的这个上下文保存到ServletContext中,供以后检索和使用。

下面来详细解释下:

image
  • DispatchServlet名如其义,它的本质上是一个Servlet,子类不断的对HttpServlet父类进行方法扩展
  • HttpServlet有两大核心方法:init()和service()方法。HttpServletBean重写了init()方法,在这部分,我们可以看到其实现思路:公共的部分统一来实现,变化的部分统一来抽象,交给其子类来实现,故用了abstract class来修饰类名。此外,HttpServletBean提供了一个HttpServlet的抽象实现,使的Servlet不再关心init-param部分的赋值,让servlet更关注于自身Bean初始化的实现
  • FrameworkServlet提供了整合web javabean和spring application context的整合方案。在源码中可以看到通过执行initWebApplicationContext()方法和initFrameworkServlet()方法实现
  • DispatchServlet是HTTP请求的中央调度处理器,它将web请求转发给controller层处理,它提供了敏捷的映射和异常处理机制,DispatchServlet转发请求的核心代码在doService()方法中实现

2.1 HttpServletBean

image
image

上图是抽象类HttpServletBean的实现,我们知道HttpServlet有两大核心方法:init()和service()方法。HttpServletBean重写了init()方法,在这部分,我们可以看到其实现思路:公共的部分统一来实现,变化的部分统一来抽象,交给其子类来实现,故用了abstract class来修饰类名。此外,HttpServletBean提供了一个HttpServlet的抽象实现,使的Servlet不再关心init-param部分的赋值,让servlet更关注于自身Bean初始化的实现。

2.2 FrameworkServlet

在HttpServletBean中有一方法initServletBean()需要子类去实现,因此 FrameworkServlet需要实现initServletBean()方法

image
image
image

2.3 DispatcherServlet

在FrameworkServlet中有一方法onRefresh()需要子类去实现,因此DispatcherServlet需要去实现onRefresh()方法。

image
image

另外一些MVC的特性初始化时在initStrategies()中实现的,包括支持国际化的LocalResolver、支持Request映射的HandlerMappings,以及视图生成的ViewResolver等等。

比如初始化HandlerMappings,在SpringMVC中,HandlerMappings的作用是为Http请求找到对应的Controller控制器,来进一步处理请求。

注意:

在web.xml配置文件中,DispatcherServlet中有一配置<load-on-startup>1</load-on-startup>,表示在容器启动的时候就加载该servlet。

当<load-on-startup>值为0或者大于0时,表示容器在应用启动时就加载这个servlet(实例化并调用其init()方法)。

当是一个负数时或者没有指定时,则表示第一次请求该servlet才加载。

正数的值越小,启动该servlet的优先级越高。

3. DispatcherServlet类和ContextLoaderListener类的关系图:

image

小结:首先,用ContextLoaderListener初始化上下文,接着使用DispatchServlet来初始化WebMVC的上下文。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容