Tomcat源碼分析(類加載與類加載器)


Tomcat的挑戰

Tomcat上可以部署多個項目

Tomcat的一般部署,可以通過多種方式啟動一個Tomcat部署多個項目,那么Tomcat在設計時會遇到什么挑戰呢?

Tomcat運行時需要加載哪些類

Tomcat中的多個項目可能存在相同的類

Tomcat中類加載的挑戰

源碼分析徹底弄懂Tomcat的類加載

類加載與類加載器

類加載

類加載:主要是將.class文件中的二進制字節讀入到JVM

我們可以看到因為這個定義,所以並沒有規定一定是要磁盤加載文件,可以通過網絡,內存什么的加載。只要是二進制流字節數據,JVM就認。

類加載過程:

1.通過類的全限定名獲取該類的二進制字節流;

2.將字節流所代表的靜態結構轉化為方法區的運行時數據結構

3.在內存中生成一個該類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口

類加載器

定義:JVM設計者把類加載這個動作放到java虛擬機外部去實現,以便讓應用程序決定如何獲取所需要的類。實現這個動作的代碼模塊成為類加載器

類與類加載器

對於任何一個類,都需要由加載它的類加載器和這個類來確定其在JVM中的唯一性。也就是說,兩個類來源於同一個Class文件,並且被同一個類加載器加載,這兩個類才相等。

注意:這里所謂的“相等”,一般使用instanceof關鍵字做判斷。

類加載器與雙親委派模型

類加載器

啟動類加載器:該加載器使用C++語言實現,屬於虛擬機自身的一部分。

啟動類加載器(Bootstrap ClassLoader:負責加載JAVA_HOME\lib目錄中並且能被虛擬機識別的類庫加載到JVM內存中,如果名稱不符合的類庫即使在lib目錄中也不會被加載。該類加載器無法被java程序直接引用

拓展類加載器與應用程序類加載器:另一部分就是所有其它的類加載器,這些類加載器是由Java語言實現,獨立於JVM外部,並且全部繼承抽象類java.lang.ClassLoader

擴展類加載器(Extension ClassLoader):該加載器主要負責加載JAVA_HOME\lib\ext目錄中的類庫,開發者可以使用擴展加載器。

應用程序類加載器(Application ClassLoader:該列加載器也稱為系統加載器,它負責加載用戶類路徑(Classpath)上所指定的類庫,開發者可以直接使用該類加載器,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器

雙親委派模型

定義:雙親委派模型的工作過程為:如果一個類加載器收到了類請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父加載器去完成,每一層都是如此,因此所有類加載的請求都會傳到啟動類加載器,只有當父加載器無法完成該請求時,子加載器才去自己加載。

實現方式:該模型要求除了頂層的啟動類加載器外,其余的類加載器都應當有自己的父類加載器。子類加載器不是以繼承的關系來實現,而是通過組合關系來復用父加載器的代碼。

意義:好處雙親委派模型的好處就是java類隨着它的類加載器一起具備了一種帶有優先級的層次關系。例如:Object,無論那個類加載器去加載該類,最終都是由啟動類加載器進行加載的,因此Object類在程序的各種類加載環境中都是一個類。如果不用改模型,那么java.lang.Object類存放在classpath中,那么系統中就會出現多個Object類,程序變得很混亂。

雙親委派模型

    從虛擬機的角度來說,有兩種不同的類加載器:一種是啟動類加載器(Bootstrap ClassLoader,該加載器使用C++語言實現,屬於虛擬機自身的一部分。另一部分就是所有其它的類加載器,這些類加載器是由Java語言實現,獨立於JVM外部,並且全部繼承抽象類java.lang.ClassLoader.

    java開發人員的角度看,大部分java程序會用到以下三種系統提供的類加載器:

    1、啟動類加載器(Bootstrap ClassLoader:負責加載JAVA_HOME\lib目錄中並且能被虛擬機識別的類庫加載到JVM內存中,如果名稱不符合的類庫即使在lib目錄中也不會被加載。該類加載器無法被java程序直接引用。

    2、擴展類加載器(Extension ClassLoader):該加載器主要負責加載JAVA_HOME\lib\ext目錄中的類庫,開發者可以使用擴展加載器。  

     3、應用程序類加載器(Application ClassLoader:該列加載器也稱為系統加載器,它負責加載用戶類路徑(Classpath)上所指定的類庫,開發者可以直接使用該類加載器,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。

JVM中的類加載器源碼分析

AppClassLoader

 

 

 

URLClassLoader

 

 

SecureClassLoader

 

 

Tomcat中的類加載解決方案

Tomcat類加載的考慮

隔離性

Web應用類庫相互隔離,避免依賴庫或者應用包相互影響,比如有兩個Web應用,一個采用了Spring 4,一個采用了Spring 5,而如果如果采用同一個類加載器,那么Web應用將會由於jar包覆蓋而無法啟動成功。

 

 

 

 

靈活性

因為隔離性,所以Web應用之間的類加載器相互獨立,如果一個Web應用重新部署時,該應用的類加載器重新加載,同時不會影響其他web應用

比如:不需要重啟Tomcat的創建xml文件的類加載,

還有context元素中的reloadable字段:如果設置為true的話,Tomcat將檢測該項目是否變更,當檢測到變更時,自動重新加載Web應用。

性能

由於每一個Web應用都有一個類加載器,所以在Web應用在加載類時,不會搜索其他Web應用包含的Jar包,性能自然高於只有一個類加載的情況。

 

Tomcat中的類加載器

Tomcat提供3個基礎類加載器(commoncatalinashared)和Web應用類加載器。

 

 

 

 

Tomcat中的類加載源碼分析

三個基礎類加載器

介紹

3個基礎類加載器的加載路徑在catalina.properties配置,默認情況下,3個基礎類加載器的實例都是一個。

createClassLoader調用ClassLoaderFactory屬於一種工廠模式,並且都是使用URLClassLoader

 

 

 

 

 

默認情況三個是一個實例,但是可以通過修改配置創建3個不同的類加載機制,使它們各司其職。

舉個例子:如果我們不想實現自己的會話存儲方案,並且這個方案依賴了一些第三方包,我們不希望這些包對Web應用可見,因此我們可以配置server.loader,創建獨立的Catalina類加載器。

共享性:

Tomcat通過Common類加載器實現了Jar包在應用服務器與Web應用之間的共享,

通過Shared類加載器實現了Jar包在Web應用之間的共享

通過Catalina類加載器加載服務器依賴的類。

 

 

類加載工廠

因為類加載需要做很多事情,比如讀取字節數組、驗證、解析、初始化等。而Java提供的URLClassLoader類能夠方便的將JarClass或者網絡資源加載到內存中。而Tomcat中則用一個工廠類,ClassLoaderFactory把創建類加載器的細節進行封裝,可以通過它方便的創建自定義類加載器。

 

 

 

 

 

 

 

 

 

 

 

 

使用加載器工廠的好處

  1. ClassLoadFactory有一個內部Repository,它就是表示資源的類,資源的類型用一個RepositoryType的枚舉表示。
  2. 同時我們看到,如果在檢查jar包的時候,如果有檢查的URL地址的如果檢查有異常就忽略掉,可以確保部分類加載正確。

 

盡早設置線程上下文類加載器

 

 

每一個運行線程中有一個成員ContextClassLoader,用於在運行時動態載入其他類,當程序中沒有顯示聲明由哪個類加載器去加載哪個類(比如new出一個類時),將默認由當前線程類加載器加載,所以一般系統默認的ContextClassLoad是系統類加載器。

一般在實際的系統上,使用線程上下文類加載器,可以設置不同的加載方式,這個也是Java靈活的類加載方式的體現,也可以很輕松的打破雙親委派模式,同時也會避免類加載的異常。

Webapp類加載器

每個web應用會對一個Context節點,在JVM中就會對應一個org.apache.catalina.core.StandardContext對象,而每一個StandardContext對象內部都一個加載器實例loader實例變量。這個loader實際上是WebappLoader對象。而每一個 WebappLoader 對象內部關聯了一個 classLoader 變量(就這這個類的定義中,可以看到該變量的類型是org.apache.catalina.loader.WebappClassLoader)。

所以,這里一個web應用會對應一個StandardContext 一個WebappLoader 一個WebappClassLoader

一個web應用對應着一個StandardContext實例,每個web應用都擁有獨立web應用類加載器(WebappClassLoader),這個類加載器在StandardContext.startInternal()中被構造了出來

 

 

 

 

注意這里:設置加載器和獲取加載器都使用了讀寫鎖機制,確保多線程情況下對共享資源的訪問不會出現問題。

同時因為Tomcat的生命周期管理,必定會調用WebappLoader.javastartInternal()方法,該方法中new出了

 

 

 

所以總結一句話,如果沒有弄懂Tomcat的啟動流程,以及弄懂Tomcat的生命周期的管理,很多地方的源碼是沒有辦法沒有看懂,所以看源碼也有一個先后順序。

熱加載源碼分析

當配置信息中有reloadable的屬性,並且值為true時,Tomcat怎么去完成這個過程呢?

 

 

還是看源碼,據Tomcat的啟動流程,我們分析下Context的初始化start方法,根據之前的課程我們可知道,Context只是一個接口,具體實現類是StandardContext,我們分析下startInternal方法(此方法由之前的抽閑骨架類中的start方法觸發)

 

 

我發現有一個線程啟動的方法 threadStart(),

 

 

由上圖我們知道,這個是父類中調用,

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM