Tomcat 類(lèi)加載機(jī)制

2022-03-03 11:45 更新

概述

與很多服務(wù)器應(yīng)用一樣,Tomcat 也安裝了各種類(lèi)加載器(那就是實(shí)現(xiàn)了 java.lang.ClassLoader 的類(lèi))。借助類(lèi)加載器,容器的不同部分以及運(yùn)行在容器上的 Web 應(yīng)用就可以訪問(wèn)不同的倉(cāng)庫(kù)(保存著可使用的類(lèi)和資源)。這個(gè)機(jī)制實(shí)現(xiàn)了 Servlet 規(guī)范 2.4 版(尤其是 9.4 節(jié)和 9.6 節(jié))里所定義的功能。

在 Java 環(huán)境中,類(lèi)加載器的布局結(jié)構(gòu)是一種父子樹(shù)的形式。通常,類(lèi)加載器被請(qǐng)求加載一個(gè)特定的類(lèi)或資源時(shí),它會(huì)先把這一請(qǐng)求委托給它的父類(lèi)加載器,只有(一個(gè)或多個(gè))父類(lèi)加載器無(wú)法找到請(qǐng)求的類(lèi)或資源時(shí),它才開(kāi)始查看自身的倉(cāng)庫(kù)。注意,Web 應(yīng)用的類(lèi)加載器模式跟這個(gè)稍有不同,下文將詳細(xì)介紹,但基本原理是一樣。

當(dāng) Tomcat 啟動(dòng)后,它就會(huì)創(chuàng)建一組類(lèi)加載器,這些類(lèi)加載器被布局成如下圖所示這種父子關(guān)系,父類(lèi)加載器在子類(lèi)加載器之上:

      Bootstrap
          |
       System
          |
       Common
       /     \
  Webapp1   Webapp2 ...

接下來(lái),通過(guò)幾節(jié)內(nèi)容來(lái)詳細(xì)說(shuō)明每一個(gè)類(lèi)加載器的特點(diǎn),其中還將講解這些加載器可使用的類(lèi)和資源的來(lái)源。

類(lèi)加載器定義

如上圖所示,Tomcat 在初始化時(shí)會(huì)創(chuàng)建如下這些類(lèi)加載器:

  • Bootstrap 這種類(lèi)加載器包含 JVM 所提供的基本的運(yùn)行時(shí)類(lèi),以及來(lái)自系統(tǒng)擴(kuò)展目錄($JAVA_HOME/jre/lib/ext)里 JAR 文件中的類(lèi)。注意:在有些 JVM 的實(shí)現(xiàn)中,它的作用不僅僅是類(lèi)加載器,或者它可能根本不可見(jiàn)(作為類(lèi)加載器)。

  • System 這種類(lèi)加載器通常是根據(jù) CLASSPATH 環(huán)境變量?jī)?nèi)容進(jìn)行初始化的。所有的這些類(lèi)對(duì)于 Tomcat 內(nèi)部類(lèi)以及 Web 應(yīng)用來(lái)說(shuō)都是可見(jiàn)的。不過(guò),標(biāo)準(zhǔn)的 Tomcat 啟動(dòng)腳本($CATALINA_HOME/bin/catalina.sh%CATALINA_HOME%\bin\catalina.bat)完全忽略了 CLASSPATH 環(huán)境變量自身的內(nèi)容,相反從下列倉(cāng)庫(kù)來(lái)構(gòu)建系統(tǒng)類(lèi)加載器:

    • $CATALINA_HOME/bin/bootstrap.jar 包含用來(lái)初始化 Tomcat 服務(wù)器的 main() 方法,以及它所依賴(lài)的類(lèi)加載器實(shí)現(xiàn)類(lèi)。
    • $CATALINA_BASE/bin/tomcat-juli.jar$CATALINA_HOME/bin/tomcat-juli.jar 日志實(shí)現(xiàn)類(lèi)。其中包括了對(duì) java.util.logging API 的功能增強(qiáng)類(lèi)(Tomcat JULI),以及對(duì) Tomcat 內(nèi)部使用的 Apache Commons 日志庫(kù)的包重命名副本。詳情參看 Tomcat 日志文檔。

      如果 *$CATALINA_BASE/bin* 中存在 `tomcat-juli.jar`,就不會(huì)使用 *$CATALINA_HOME/bin* 中的那一個(gè)。它有助于日志的特定配置。   
    • $CATALINA_HOME/bin/commons-daemon.jar Apache Commons Daemon 項(xiàng)目的類(lèi)。該 JAR 文件并不存在于由 catalina.batcatalina.sh 腳本所創(chuàng)建的 CLASSPATH 中,而是引用自 bootstrap.jar 的清單文件。
  • Common 這種類(lèi)加載器包含更多的額外類(lèi),它們對(duì)于Tomcat 內(nèi)部類(lèi)以及所有 Web 應(yīng)用都是可見(jiàn)的。

    通常,應(yīng)用類(lèi)不會(huì)放在這里。該類(lèi)加載器所搜索的位置定義在 $CATALINA_BASE/conf/catalina.propertiescommon.loader 屬性中。默認(rèn)的設(shè)置會(huì)搜索下列位置(按照列表中的上下順序)。

    • $CATALINA_BASE/lib 中的解包的類(lèi)和資源。
    • $CATALINA_BASE/lib 中的 JAR 文件。
    • $CATALINA_HOME/lib 中的解包類(lèi)和資源。
    • $CATALINA_HOME/lib 中的 JAR 文件。

      默認(rèn),它包含以下這些內(nèi)容:

    • annotations-api.jar JavaEE 注釋類(lèi)。
    • catalina.jar Tomcat 的 Catalina servlet 容器部分的實(shí)現(xiàn)。
    • catalina-ant.jar Tomcat Catalina Ant 任務(wù)。
    • catalina-ha.jar 高可用性包。
    • catalina-storeconfig.jar
    • catalina-tribes.jar 組通信包
    • ecj-*.jar Eclipse JDT Java 編譯器
    • el-api.jar EL 3.0 API
    • jasper.jar Tomcat Jasper JSP 編譯器與運(yùn)行時(shí)
    • jasper-el.jar Tomcat Jasper EL 實(shí)現(xiàn)
    • jsp-api.jar JSP 2.3 API
    • servlet-api.jar Servlet 3.1 API
    • tomcat-api.jar Tomcat 定義的一些接口
    • tomcat-coyote.jar Tomcat 連接器與工具類(lèi)。
    • tomcat-dbcp.jar 數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn),基于 Apache Commons Pool 的包重命名副本和 Apache Commons DBCP。
    • tomcat-i18n-**.jar 包含其他語(yǔ)言資源束的可選 JAR。因?yàn)槟J(rèn)的資源束也可以包含在每個(gè)單獨(dú)的 JAR 文件中,所以如果不需要國(guó)際化信息,可以將其安全地移除。
    • tomcat-jdbc.jar 一個(gè)數(shù)據(jù)庫(kù)連接池替代實(shí)現(xiàn),又被稱(chēng)作 Tomcat JDBC 池。詳情參看 JDBC 連接池文檔。
    • tomcat-util.jar Apache Tomcat 多種組件所使用的常用類(lèi)。
    • tomcat-websocket.jar WebSocket 1.1 實(shí)現(xiàn)
    • websocket-api.jar WebSocket 1.1 API
  • WebappX 為每個(gè)部署在單個(gè) Tomcat 實(shí)例中的 Web 應(yīng)用創(chuàng)建的類(lèi)加載器。你的 Web 應(yīng)用的 /WEB-INF/classes 目錄中所有的解包類(lèi)及資源,以及 /WEB-INF/lib 目錄下 JAR 文件中的所有類(lèi)及資源,對(duì)于該應(yīng)用而言都是可見(jiàn)的,但對(duì)于其他應(yīng)用來(lái)說(shuō)則不可見(jiàn)。

如上所述,Web 應(yīng)用類(lèi)加載器背離了默認(rèn)的 Java 委托模式(根據(jù) Servlet 規(guī)范 2.4 版的 9.7.2 Web Application Classloader一節(jié)中提供的建議)。當(dāng)某個(gè)請(qǐng)求想從 Web 應(yīng)用的 WebappX 類(lèi)加載器中加載類(lèi)時(shí),該類(lèi)加載器會(huì)查看自己的倉(cāng)庫(kù),而不是預(yù)先進(jìn)行委托處理。There are exceptions。JRE 基類(lèi)的部分類(lèi)不能被重寫(xiě)。對(duì)于一些類(lèi)(比如 J2SE 1.4+ 的 XML 解析器組件),可以使用 J2SE 1.4 支持的特性。最后,類(lèi)加載器會(huì)顯式地忽略所有包含 Servlet API 類(lèi)的 JAR 文件,所以不要在 Web 應(yīng)用包含任何這樣的 JAR 文件。Tomcat 其他的類(lèi)加載器則遵循常用的委托模式。

因此,從 Web 應(yīng)用的角度來(lái)看,加載類(lèi)或資源時(shí),要查看的倉(cāng)庫(kù)及其順序如下:

  • JVM 的 Bootstrap 類(lèi)
  • Web 應(yīng)用的 /WEB-INF/classes 類(lèi)
  • Web 應(yīng)用的 /WEB-INF/lib/*.jar 類(lèi)
  • System 類(lèi)加載器的類(lèi)(如上所述)
  • Common 類(lèi)加載器的類(lèi)(如上所述)

如果 Web 應(yīng)用類(lèi)加載器配置有 <Loader delegate="true"/>,則順序變?yōu)椋?/p>

  • JVM 的 Bootstrap 類(lèi)
  • System 類(lèi)加載器的類(lèi)(如上所述)
  • Common 類(lèi)加載器的類(lèi)(如上所述)
  • Web 應(yīng)用的 /WEB-INF/classes 類(lèi)
  • Web 應(yīng)用的 /WEB-INF/lib/*.jar 類(lèi)

XML解析器和 Java

從 Java 1.4 版起,JRE 就包含了一個(gè) JAXP API 的副本和一個(gè) XML 解析器。這對(duì)希望使用自己的 XML 解析器的應(yīng)用產(chǎn)生了一定的影響。

在過(guò)去的 Tomcat 中,你只需在 Tomcat 庫(kù)中簡(jiǎn)單地?fù)Q掉 XML 解析器,就能改變所有 Web 應(yīng)用使用的解析器。但對(duì)于現(xiàn)在版本的 Java 而言,這一技術(shù)并沒(méi)有效果,因?yàn)橥ǔ5念?lèi)加載器委托進(jìn)程往往會(huì)優(yōu)先選擇 JDK 內(nèi)部的實(shí)現(xiàn)。

Java 支持一種叫做“授權(quán)標(biāo)準(zhǔn)覆蓋機(jī)制”,從而能夠替換在 JCP 之外創(chuàng)建的 API(例如 W3C 的 DOM 和 SAX)。它還可以用于更新 XML 解析器實(shí)現(xiàn)。關(guān)于此機(jī)制的詳情,請(qǐng)參看 http://docs.oracle.com/javase/1.5.0/docs/guide/standards/index.html。

為了利用該機(jī)制,Tomcat 在啟動(dòng)容器的命令行中包含了系統(tǒng)屬性設(shè)置 -Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS。該選項(xiàng)的默認(rèn)值為 $CATALINA_HOME/endorsed。但要注意,這個(gè) endorsed 目錄并非默認(rèn)創(chuàng)建的。

安全管理器下運(yùn)行

當(dāng)在安全管理器下運(yùn)行時(shí),類(lèi)被允許加載的位置也是基于策略文件中的內(nèi)容,詳情可查看 安全管理器文檔。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)