與很多服務(wù)器應(yīng)用一樣,Tomcat 也安裝了各種類加載器(那就是實現(xiàn)了 java.lang.ClassLoader
的類)。借助類加載器,容器的不同部分以及運行在容器上的 Web 應(yīng)用就可以訪問不同的倉庫(保存著可使用的類和資源)。這個機制實現(xiàn)了 Servlet 規(guī)范 2.4 版(尤其是 9.4 節(jié)和 9.6 節(jié))里所定義的功能。
在 Java 環(huán)境中,類加載器的布局結(jié)構(gòu)是一種父子樹的形式。通常,類加載器被請求加載一個特定的類或資源時,它會先把這一請求委托給它的父類加載器,只有(一個或多個)父類加載器無法找到請求的類或資源時,它才開始查看自身的倉庫。注意,Web 應(yīng)用的類加載器模式跟這個稍有不同,下文將詳細(xì)介紹,但基本原理是一樣。
當(dāng) Tomcat 啟動后,它就會創(chuàng)建一組類加載器,這些類加載器被布局成如下圖所示這種父子關(guān)系,父類加載器在子類加載器之上:
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 ...
接下來,通過幾節(jié)內(nèi)容來詳細(xì)說明每一個類加載器的特點,其中還將講解這些加載器可使用的類和資源的來源。
如上圖所示,Tomcat 在初始化時會創(chuàng)建如下這些類加載器:
Bootstrap 這種類加載器包含 JVM 所提供的基本的運行時類,以及來自系統(tǒng)擴展目錄($JAVA_HOME/jre/lib/ext
)里 JAR 文件中的類。注意:在有些 JVM 的實現(xiàn)中,它的作用不僅僅是類加載器,或者它可能根本不可見(作為類加載器)。
System 這種類加載器通常是根據(jù) CLASSPATH
環(huán)境變量內(nèi)容進(jìn)行初始化的。所有的這些類對于 Tomcat 內(nèi)部類以及 Web 應(yīng)用來說都是可見的。不過,標(biāo)準(zhǔn)的 Tomcat 啟動腳本($CATALINA_HOME/bin/catalina.sh
或 %CATALINA_HOME%\bin\catalina.bat
)完全忽略了 CLASSPATH
環(huán)境變量自身的內(nèi)容,相反從下列倉庫來構(gòu)建系統(tǒng)類加載器:
$CATALINA_HOME/bin/bootstrap.jar
包含用來初始化 Tomcat 服務(wù)器的 main()
方法,以及它所依賴的類加載器實現(xiàn)類。 $CATALINA_BASE/bin/tomcat-juli.jar
或 $CATALINA_HOME/bin/tomcat-juli.jar
日志實現(xiàn)類。其中包括了對 java.util.logging
API 的功能增強類(Tomcat JULI),以及對 Tomcat 內(nèi)部使用的 Apache Commons 日志庫的包重命名副本。詳情參看 Tomcat 日志文檔。
如果 *$CATALINA_BASE/bin* 中存在 `tomcat-juli.jar`,就不會使用 *$CATALINA_HOME/bin* 中的那一個。它有助于日志的特定配置。
$CATALINA_HOME/bin/commons-daemon.jar
Apache Commons Daemon 項目的類。該 JAR 文件并不存在于由 catalina.bat
或 catalina.sh
腳本所創(chuàng)建的 CLASSPATH
中,而是引用自 bootstrap.jar 的清單文件。 Common 這種類加載器包含更多的額外類,它們對于Tomcat 內(nèi)部類以及所有 Web 應(yīng)用都是可見的。
通常,應(yīng)用類不會放在這里。該類加載器所搜索的位置定義在 $CATALINA_BASE/conf/catalina.properties
的 common.loader
屬性中。默認(rèn)的設(shè)置會搜索下列位置(按照列表中的上下順序)。
$CATALINA_BASE/lib
中的解包的類和資源。 $CATALINA_BASE/lib
中的 JAR 文件。 $CATALINA_HOME/lib
中的解包類和資源。 $CATALINA_HOME/lib
中的 JAR 文件。
默認(rèn),它包含以下這些內(nèi)容:
/WEB-INF/classes
目錄中所有的解包類及資源,以及 /WEB-INF/lib
目錄下 JAR 文件中的所有類及資源,對于該應(yīng)用而言都是可見的,但對于其他應(yīng)用來說則不可見。 如上所述,Web 應(yīng)用類加載器背離了默認(rèn)的 Java 委托模式(根據(jù) Servlet 規(guī)范 2.4 版的 9.7.2 Web Application Classloader一節(jié)中提供的建議)。當(dāng)某個請求想從 Web 應(yīng)用的 WebappX 類加載器中加載類時,該類加載器會先查看自己的倉庫,而不是預(yù)先進(jìn)行委托處理。There are exceptions。JRE 基類的部分類不能被重寫。對于一些類(比如 J2SE 1.4+ 的 XML 解析器組件),可以使用 J2SE 1.4 支持的特性。最后,類加載器會顯式地忽略所有包含 Servlet API 類的 JAR 文件,所以不要在 Web 應(yīng)用包含任何這樣的 JAR 文件。Tomcat 其他的類加載器則遵循常用的委托模式。
因此,從 Web 應(yīng)用的角度來看,加載類或資源時,要查看的倉庫及其順序如下:
/WEB-INF/classes
類 /WEB-INF/lib/*.jar
類如果 Web 應(yīng)用類加載器配置有 <Loader delegate="true"/>
,則順序變?yōu)椋?
/WEB-INF/classes
類 /WEB-INF/lib/*.jar
類 從 Java 1.4 版起,JRE 就包含了一個 JAXP API 的副本和一個 XML 解析器。這對希望使用自己的 XML 解析器的應(yīng)用產(chǎn)生了一定的影響。
在過去的 Tomcat 中,你只需在 Tomcat 庫中簡單地?fù)Q掉 XML 解析器,就能改變所有 Web 應(yīng)用使用的解析器。但對于現(xiàn)在版本的 Java 而言,這一技術(shù)并沒有效果,因為通常的類加載器委托進(jìn)程往往會優(yōu)先選擇 JDK 內(nèi)部的實現(xiàn)。
Java 支持一種叫做“授權(quán)標(biāo)準(zhǔn)覆蓋機制”,從而能夠替換在 JCP 之外創(chuàng)建的 API(例如 W3C 的 DOM 和 SAX)。它還可以用于更新 XML 解析器實現(xiàn)。關(guān)于此機制的詳情,請參看 http://docs.oracle.com/javase/1.5.0/docs/guide/standards/index.html。
為了利用該機制,Tomcat 在啟動容器的命令行中包含了系統(tǒng)屬性設(shè)置 -Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS
。該選項的默認(rèn)值為 $CATALINA_HOME/endorsed
。但要注意,這個 endorsed
目錄并非默認(rèn)創(chuàng)建的。
當(dāng)在安全管理器下運行時,類被允許加載的位置也是基于策略文件中的內(nèi)容,詳情可查看 安全管理器文檔。