前一篇文章講了org.apache.catalina.startup.HostConfig
的 lifecycleEvent 方法中所作的事情。最後看到在 Tomcat 啓動時或啓動後(後臺線程定時掃描)會調用 HostConfig 類的 deployApps 方法: java
-startStop-
的線程還會運行一段時間才結束。但瀏覽這三種部署方式的實現代碼,裏面都是構建一個 Context 對象,並將構建好的 Context 對象與 Host 組件關聯起來(即調用
host.addChild(context)
這句,具體代碼在 HostConfig 類的
deployDescriptor(ContextName cn, File contextXml)
、
deployDirectory(ContextName cn, File dir)
、
deployWAR(ContextName cn, File war)
三個方法中,這裏再也不貼出代碼來詳細分析)。
前一篇文章只分析到這步,能夠看出與一個 web 應用相對應的一個 Context 對象已經構建出來了,但若是容器只執行到這裏根本沒法響應一個瀏覽器的一次請求。就 web 服務器的實現來看一次請求過來除了須要根據內部 Context 構建找到此次請求訪問的web應用具體所對應的 Context 對象,還須要包含 web 應用中具體的哪一個 Servlet 來處理此次請求,中間是否還須要執行相應的過濾器( filter )、監聽器( listener )等,作過 java 的 web 開發的同窗都知道,這些信息是配置在一個 web 應用的WEB-INF\web.xml
文件的(servlet3 中已經支持將這些配置信息放到 Java 文件的註解中,但萬變不離其宗,總歸要在 web 應用的某個地方說明,並在容器啓動時加載,這樣才能真正提供 web 服務,響應請求)。web
看到這裏能夠猜到 Tomcat 容器加載 web 應用時一定會有對於每一個應用的 web.xml 文件的解析過程,本文就來看看這個解析過程。apache
在本文開頭提到的三種部署應用的實現代碼中有一些共通的代碼,這裏摘出來講明一下:瀏覽器
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
context.addLifecycleListener(listener);
複製代碼
host.addChild(context);
複製代碼
第一段是在全部 Context 對象構建時會添加一個監聽器,這裏監聽器的類名是 StandardHost 類的實例變量 configClass ,其默認值就是org.apache.catalina.startup.ContextConfig
。第二段是將當前構建的 Context 對象添加到父容器 Host 對象中。bash
先看 StandardHost 的 addChild 方法的實現: 服務器
能夠看到這段代碼最後調用了父類的 addChild 方法: 這裏看下 addChildInternal 方法的實現: 能夠看到會調用子容器的 start 方法,就是指調用 StandardContext 的 start 方法。即給 host 對象添加子容器時將會調用子容器的 start 方法,按照前面文章的分析,調用 StandardContext 的 start 方法最終會調用org.apache.catalina.core.StandardContext
類的 startInternal 方法(該方法代碼較長,建議本身閱讀,再也不貼出),這裏將會發布一系列事件,按調用先後順序這些事件包括:BEFORE_INIT_EVENT
、AFTER_INIT_EVENT
、BEFORE_START_EVENT
、CONFIGURE_START_EVENT
、START_EVENT
、AFTER_START_EVENT
。post
前面提到在構建 Context 對象時都會註冊一個監聽器org.apache.catalina.startup.ContextConfig
,看下這個類的 lifecycleEvent 方法中(爲何會執行這個方法能夠看這篇文章的分析)監聽了哪些事件: spa
AFTER_INIT_EVENT
(執行 init 方法)、
BEFORE_START_EVENT
(執行 beforeStart 方法)、
CONFIGURE_START_EVENT
(執行 configureStart 方法)。
在 configureStart 方法將直接調用 webConfig 方法,正是在這個方法中將會解析 web.xml 文件: 線程
這個方法裏面作的事情,在英文註釋中說的很清楚了,歸納起來包括合併 Tomcat 全局 web.xml 、當前應用中的 web.xml 、web-fragment.xml 和 web 應用的註解中的配置信息,並將解析出的各類配置信息(如 servlet 配置、filter 配置等)關聯到 Context 對象中(在上面的代碼第 140 行:webXml.configureContext(context)
)。
看下 configureContext 方法: 3d
能夠看到裏面對 context 調用了各類 set、add 方法,從而將 web.xml 中的各類配置信息與表示一個 web 應用的 context 對象關聯起來。