說到本篇的tomcat類載入機制,不得不說翻譯學習tomcat的初衷。
之前實習的時候學習javaMelody的原始碼,但是它是一個Maven的專案,與我們自己的web專案整合後無法直接斷點除錯。後來同事指導,說是直接把java類複製到src下就可以了。很納悶....為什麼會優先載入src下的java檔案(編譯出的class),而不是jar包中的class呢?
現在瞭解tomcat的類載入機制,原來一切是這麼的簡單。
類載入
在JVM中並不是一次性把所有的檔案都載入到,而是一步一步的,按照需要來載入。
比如JVM啟動時,會通過不同的類載入器載入不同的類。當使用者在自己的程式碼中,需要某些額外的類時,再通過載入機制載入到JVM中,並且存放一段時間,便於頻繁使用。
因此使用哪種類載入器、在什麼位置載入類都是JVM中重要的知識。
JVM類載入
JVM類載入採用 父類委託機制,如下圖所示:
JVM中包括集中類載入器:
1 BootStrapClassLoader 引導類載入器
2 ExtClassLoader 擴充套件類載入器
3 AppClassLoader 應用類載入器
4 CustomClassLoader 使用者自定義類載入器
他們的區別上面也都有說明。需要注意的是,不同的類載入器載入的類是不同的,因此如果使用者載入器1載入的某個類,其他使用者並不能夠使用。
當JVM執行過程中,使用者需要載入某些類時,會按照下面的步驟(父類委託機制):
1 使用者自己的類載入器,把載入請求傳給父載入器,父載入器再傳給其父載入器,一直到載入器樹的頂層。
2 最頂層的類載入器首先針對其特定的位置載入,如果載入不到就轉交給子類。
3 如果一直到底層的類載入都沒有載入到,那麼就會丟擲異常ClassNotFoundException。
因此,按照這個過程可以想到,如果同樣在CLASSPATH指定的目錄中和自己工作目錄中存放相同的class,會優先載入CLASSPATH目錄中的檔案。
Tomcat類載入
在tomcat中類的載入稍有不同,如下圖:
當tomcat啟動時,會建立幾種類載入器:
1 Bootstrap 引導類載入器
載入JVM啟動所需的類,以及標準擴充套件類(位於jre/lib/ext下)
2 System 系統類載入器
載入tomcat啟動的類,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位於CATALINA_HOME/bin下。
3 Common 通用類載入器
載入tomcat使用以及應用通用的一些類,位於CATALINA_HOME/lib下,比如servlet-api.jar
4 webapp 應用類載入器
每個應用在部署後,都會建立一個唯一的類載入器。該類載入器會載入位於 WEB-INF/lib下的jar檔案中的class 和 WEB-INF/classes下的class檔案。
當應用需要到某個類時,則會按照下面的順序進行類載入:
1 使用bootstrap引導類載入器載入
2 使用system系統類載入器載入
3 使用應用類載入器在WEB-INF/classes中載入
4 使用應用類載入器在WEB-INF/lib中載入
5 使用common類載入器在CATALINA_HOME/lib中載入
問題擴充套件
通過對上面tomcat類載入機制的理解,就不難明白 為什麼java檔案放在Eclipse中的src資料夾下會優先jar包中的class?
這是因為Eclipse中的src資料夾中的檔案java以及webContent中的JSP都會在tomcat啟動時,被編譯成class檔案放在 WEB-INF/class 中。
而Eclipse外部引用的jar包,則相當於放在 WEB-INF/lib 中。
因此肯定是 java檔案或者JSP檔案編譯出的class優先載入。
通過這樣,我們就可以簡單的把java檔案放置在src資料夾中,通過對該java檔案的修改以及除錯,便於學習擁有原始碼java檔案、卻沒有打包成xxx-source的jar包。
另外呢,開發者也會因為粗心而犯下面的錯誤。
在 CATALINA_HOME/lib 以及 WEB-INF/lib 中放置了 不同版本的jar包,此時就會導致某些情況下報載入不到類的錯誤。
還有如果多個應用使用同一jar包檔案,當放置了多份,就可能導致 多個應用間 出現類載入不到的錯誤。
參考
【1】Tomcat Class Loader:http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html
【2】Tomcat 類載入機制:http://blog.csdn.net/dc_726/article/details/11873343