程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> jetty加載spring-context容器源碼分析,jettycontextpath

jetty加載spring-context容器源碼分析,jettycontextpath

編輯:JAVA綜合教程

jetty加載spring-context容器源碼分析,jettycontextpath


帶著疑問開始

web.xml的順序問題

先拿一個最簡單的spring mvc web.xml來說問題,如下圖:如果我將三者的順序倒置或是亂置,會產生什麼結果呢?

啟動報錯?還是加載未知結果?還是毫無影響?

結果是什麼呢?讓我們用實踐來證明一下:go->jetty-spring-context project 現場演示

//todo 之後貼出結果

applicationContext.xml和spring-mvc-servlet.xml怎麼配置

最簡單的配置(這樣不僅產生兩個容器而且每個容器都生成一樣的bean)

applicationContext:

spring-mvc-servlet.xml

正確的配置其中之一

 1 applicationContext.xml
 2 <?xml version="1.0" encoding="UTF-8"?>
 3 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xmlns:context="http://www.springframework.org/schema/context"
 5        xmlns="http://www.springframework.org/schema/beans"
 6        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
 7       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
 8     <!--正確的配置-->
 9     <context:component-scan base-package="com.meituan.jetty.spring">
10         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
11     </context:component-scan>
12 </beans>
13  
14 spring-mvc-servlet.xml
15 <beans xmlns="http://www.springframework.org/schema/beans"
16        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
17        xmlns:context="http://www.springframework.org/schema/context"
18        xsi:schemaLocation="http://www.springframework.org/schema/beans
19         http://www.springframework.org/schema/beans/spring-beans.xsd
20         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
21     <!--正確的配置-->
22     <context:component-scan base-package="com.meituan.jetty.spring.controller"/>
23     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
24         <property name="prefix" value="/"/>
25         <property name="suffix" value=".jsp"/>
26     </bean>
27 </beans>

可以麼?go->jetty-spring-context project 現場演示問題來了,通過結果我們發現,確實有兩個容器,那麼由於兩個容器同時可以有一樣的beans,那是否可以直接去掉ApplicationContext容器呢?

applicationContext.xml和spring-mvc-servlet.xml的初始化順序如何

結果:applicationContext.xml的初始化優先於spring-mvc-servlet.xml

兩個context容器到底是怎樣的

下邊的圖是applicationContext接口的少部分實現

深入源碼解決疑難

為什麼不管怎麼配置app.xml總是在mvc之前初始化

下邊讓我們用源碼一步一步來分析其中的奧妙

啟動jetty容器入口

 ......................前邊還有一大段代碼

初始化調用的是mms server的start方法,其實server沒有start方法,是它的父類AbstractLifeCyle的start方法,然後再回調,我們來看下server的結構

在繼續講server是怎麼一步步調用之前,我們需要知道兩個事情

ContextLoaderListener和DispatcherServlet的實質(插播)

contextLoaderListerner實質是實現了EventListener的一個事件監聽器

相關事件通知,在我的另外一篇wiki中有詳細介紹:

事件通知機制深入源碼#事件通知機制深入源碼-ApplicationListener

或者 直接看這裡:Spring事件通知機制詳解

contextLoaderListerner 的方法  contextInitialized  會被回調


DispatcherServlet實質上是一個servlet,當然,這個不用說也看的很清楚

DispatcherServlet 的父類FrameworkServlet的方法initServletBean會被回調

(警告)   這時候我們還要知道一個事情:contextLoaderListerner 和 DispatcherServlet 是在spring的兩個package裡邊,前者在spring-web裡邊,後者在spring-webmvc裡邊,這個對後邊的理解有幫助

啟動jetty容器入口(續)

為什麼listener能夠無論以哪種姿態都會優先於servlet執行呢?

要解決這個問題,我們先看下listener是在何時被回調的:

首先大概浏覽下這個圖,這裡對WebAppContext和ContextHandler大概有一個映像(當然,這個是jetty的源碼)

jetty啟動,會初始化一個WebAppContext(WebAppContext 繼承了 ServletContextHandler ,ServletContextHandler  繼承了ContextHandler ,而且他們都實現了 )對象;

最終,WebAppContext對象的startContext()方法會被實現,如下圖調用鏈:

而startContext方法又做了什麼事情呢?帶著疑問,我們走進下邊的代碼:

我們發現,ContextHandler裡邊存了一個listener的集合,而恰巧我們的 ContextLoaderListener 實例也在這個集合當中;

我們看到這裡把ContextLoaderListener和event事件傳遞給了callContextInitialized方法,所以ContextLoaderListener的contextInitialized方法最終會被調用,

到此為止,我們就解釋了ContextLoaderListener是會被合理的初始化的;

至於ContextLoaderListener初始化的詳細過程,請看這裡:淺談jetty如何初始化spring容器-ContextLoaderListener初始化context容器的過程

咦?好像有什麼不對的地方。哦,對,本來是WebAppContext的startContext方法,怎麼會跑到ContextHandler的startContext方法,看上圖,

是子類父類的關系,原來如此;

看調用鏈,再來說說 boot 本來調用server的start,為什麼會走到lifeCycle呢?

原來 server 繼承了 AbstractLifeCycle,jetty源代碼裡邊大量運用了 模板方法和類模板方法,我們開發的時候也可以學習這種設計模式,減少重復代碼,提高代碼復用率。

講了這麼多,還沒講到 為什麼 listener 總是在 servlet之前執行呢?

莫急,且聽下邊講解

如下,WebAppContext的 doStart 方法被調用,此時WebAppContext自己實現了一部分,其余直接調用父類->ContextHandler的doStart方法

(咦,不對,父類不是ServletContextHandler麼;哦,ServletContextHandler並未重寫這個方法)

接下來調用ContextHandler的doStart方法

ContextHandler再次調用子類WebAppContext的方法 startContext()

WebAppContext 首先調用 startWebApp,然後 startWebApp 再次調用父類 ServletContextHandler的 startContext方法

這裡就比較有意思了:ServletContextHandler首先調用父類,也就是ContextHandler的startContext方法,還記得父類的這個方法發生了什麼嗎?

對!父類這個方法裡邊初始化了 ContextLoadListener ,也就是初始化了所有的 事件通知 !!!

事件通知完成之後,開始調用servlet的initialize方法,初始化servlet;servlet初始化詳解:深入淺出jetty初始化spring容器-DispatcherServlet初始化context容器的過程

(信息)  也就是說:frameworkServlet初始化方法的回調是由ServletContextHandler的startContext方法引起的!!!

看下邊 listener和servlet的執行順序:

至此為止,我們剖析了 jetty初始化 為什麼 listener的執行一定會先於servlet!!!(眨眼)(眨眼)(眨眼)

ContextLoaderListener初始化context容器的過程

ContextLoaderListener結構

ContextLoaderListener入口

首先,回調ContextLoaderListener的contextInitialized方法

然後調用父類ContextLoader的contextInitialized方法,第一次初始化的時候 org.springframework.web.context.WebApplicationContext.ROOT == null

緊接著在1的時候創建context

我們看看 context 到底是怎麼創建的?創建的是哪種類型?紅框決定了創建哪種類型的 applicationContext

如下圖,通過strategy決定創建哪種類型

strategy又是怎麼初始化的呢?

look

可見,是這個配置文件決定了,wepApplicationContext的類型是XmlWebApplicationContext,接下來是configAndRefresh

最終調用 AbstractApplicationContext的refresh方法,根據配置文件內容,開始初始化;

AbstractApplicationContext的refresh的初始化都知道吧?不知道的話,可以看我這篇關於spring初始化順序的文章:Spring Init&Destroy#spring容器的主要入口

DispatcherServlet初始化context容器的過程

DispatcherServlet結構

DispatcherServlet入口

初始化webApplicationContext,

創建webApplicationContext

這裡的contextClass是這麼決定的

最終也是調用refresh實例化的

最終完成第二個容器的初始化

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved