Spring如何在Tomcat啟動的時候啟動的

?在我們使用spring跟tomcat進(jìn)行結(jié)合的時候,我們都會在Resources文件下創(chuàng)建一個webapp/WEB-INF文件夾下面創(chuàng)建一個web.xml文件,在這個文件中我們會添加這么幾行配置

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

?加上這個配置后Tomcat在啟動的時候會去讀取web.xml文件中的<listener>和<context-param>兩個結(jié)點(diǎn),接著,會創(chuàng)建一個ServletContext(servlet上下文),這個web項(xiàng)目的所有部分都將共享這個上下文,然后將<context-param>轉(zhuǎn)換為鍵值對鹤盒,并交給servletContext。 還會創(chuàng)建<listener>中的類實(shí)例侦副,創(chuàng)建監(jiān)聽器侦锯。此時就會創(chuàng)建ContextLoaderListener類,在初始化Web應(yīng)用程序中的任何過濾器或servlet之前秦驯,將調(diào)用類中的contextInitialized方法尺碰。

    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }

?這個方法會為給定的servlet上下文初始化Spring的Web應(yīng)用程序上下文,進(jìn)入到里面調(diào)用的initWebApplicationContext方法,這個方法定義在ContextLoaderListener的父類ContextLoader中

public class ContextLoader {
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        ...
        try {
            //如果WebApplicationContext還是null汇竭,一般使用web.xml的形式配置的時候葱蝗,值會為null,這時候使用的是ContextLoaderListener的默認(rèn)的空構(gòu)造器
            if (this.context == null) {
                //創(chuàng)建WebApplicationContext
                this.context = createWebApplicationContext(servletContext);
            }
            //如果WebApplicationContext是ConfigurableWebApplicationContext類型的時候會進(jìn)行一些處理
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                //如果此時的上下文還沒有激活细燎,第一次進(jìn)來的時候是沒有激活的即false,當(dāng)上下文刷新之后皂甘,這個值回時true
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as  setting the parent context, setting the application context id, etc
                    //如果父上下文還沒有設(shè)置玻驻,就將servletContext設(shè)置為父上下文,因?yàn)榇藭r很多信息都是里面后面會用到
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent ->  determine parent for root web application context, if any.
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    //配置并刷新上下文偿枕,會把servletContext中的一些屬性放到spring的上下文中璧瞬,例如如果我們配置了的servlet的initParam參數(shù)會被獲取
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
            //將spring的上下文保存到servletContext中
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
            if (ccl == ContextLoader.class.getClassLoader()) {
                currentContext = this.context;
            }
            //將上下文存儲在本地實(shí)例變量中,以保證它在ServletContext關(guān)閉時可用
            else if (ccl != null) {
                currentContextPerThread.put(ccl, this.context);
            }
            if (logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
            }

            return this.context;
            }
        ...
    }
}

?當(dāng)我們使用xml的形式配置listener的時候默認(rèn)WebApplicationContext為null渐夸,此時就會自動創(chuàng)建一個WebApplicationContext嗤锉,所以進(jìn)入到createWebApplicationContext方法

    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    //根據(jù)ServletContext來獲取WebApplicationContext的Class類型
        Class<?> contextClass = determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        //實(shí)例化WebApplicationContext
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

?接下來進(jìn)入determineContextClass方法

    protected Class<?> determineContextClass(ServletContext servletContext) {
        //如果有實(shí)現(xiàn)WebApplicationContext并設(shè)置了contextClass的值,則會使用配置的值
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        //默認(rèn)情況下是沒有配置的
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        else {
            //從defaultStrategies中獲取默認(rèn)的WebApplicationContext的ClassName
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                //根據(jù)contextClassName獲取Class對象
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }   

?一般情況下我們都不會去設(shè)置ServletContext類的contextClass的配置墓塌,所以這里會使用默認(rèn)值瘟忱,進(jìn)入到defaultStrategies這個對象奥额,發(fā)現(xiàn)這個對象在ContextLoader的靜態(tài)代碼塊中被初始化

        private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
    static {
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
        }
    }

?可以看到defaultStrategies的值是從配置文件ContextLoader.properties中獲取的,所以可以直接打開文件查看

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

?到這里就知道默認(rèn)實(shí)例化的WebApplicationContext是XmlWebApplicationContext访诱。既然上下文實(shí)例已經(jīng)創(chuàng)建了垫挨,接下來就是刷新了。這里就要進(jìn)入到initWebApplicationContext方法中的調(diào)用的configureAndRefreshWebApplicationContext方法了,主要看其中的refresh方法触菜。

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    ...
    //前面做的是為上下文刷新之前的做的準(zhǔn)備九榔,比如設(shè)置ServletContext到spring的上下文中,獲取
    //刷新上下文
    wac.refresh();
    ...
    }

?這里的就是開啟Spring的容器的刷新了涡相,也是最核心的了哲泊。這里調(diào)用的是AbstractApplicationContext類的refresh,關(guān)于這個前面已經(jīng)講解過5.2Spring源碼解析——refresh方法,注意在后面調(diào)用loadBeanDefinitions方法的時候調(diào)用的是XmlWebApplicationContext中的loadBeanDefinitions催蝗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末切威,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子生逸,更是在濱河造成了極大的恐慌牢屋,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槽袄,死亡現(xiàn)場離奇詭異烙无,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)遍尺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門截酷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乾戏,你說我怎么就攤上這事迂苛。” “怎么了鼓择?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵三幻,是天一觀的道長。 經(jīng)常有香客問我呐能,道長念搬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任摆出,我火速辦了婚禮朗徊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偎漫。我一直安慰自己爷恳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布象踊。 她就那樣靜靜地躺著温亲,像睡著了一般棚壁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上铸豁,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天灌曙,我揣著相機(jī)與錄音,去河邊找鬼节芥。 笑死在刺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的头镊。 我是一名探鬼主播蚣驼,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼相艇!你這毒婦竟也來了颖杏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坛芽,失蹤者是張志新(化名)和其女友劉穎留储,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咙轩,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡获讳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了活喊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丐膝。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖钾菊,靈堂內(nèi)的尸體忽然破棺而出帅矗,到底是詐尸還是另有隱情,我是刑警寧澤煞烫,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布浑此,位于F島的核電站,受9級特大地震影響滞详,放射性物質(zhì)發(fā)生泄漏尤勋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一茵宪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瘦棋,春花似錦稀火、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篇裁。三九已至,卻和暖如春赡若,著一層夾襖步出監(jiān)牢的瞬間达布,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工逾冬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黍聂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓身腻,卻偏偏與公主長得像产还,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嘀趟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1.Spring整體架構(gòu) 1)核心容器(Core Container) Core模塊脐区,主要包含了Spring框架基...
    Sponge1128閱讀 1,066評論 0 1
  • 前言:最近看dispatcherServlet的源碼分析,被各種context搞得想罵X她按,但抱怨過后還得看呀牛隅,于是...
    LeaveStyle閱讀 17,039評論 1 17
  • 本文從API角度入手,帶你了解SpringMVC啟動的原理酌泰。 ServletContainerInitialize...
    南橋畂翊閱讀 5,874評論 1 14
  • spring的啟動是建筑在servlet容器之上的媒佣,所有web工程的初始位置就是web.xml,它配置了servl...
    onlyHalfSoul閱讀 81,996評論 2 54
  • 3.1、DispatcherServlet作用 DispatcherServlet是前端控制器設(shè)計模式的實(shí)現(xiàn)宫莱,提供...
    秋名山車神_f776閱讀 2,933評論 0 0