Tomcat的類加載機制流程及源碼解析
在前面 Java虛擬機:對象創(chuàng)建過程與類加載機制、雙親委派模型 文章中,我們介紹了 JVM 的類加載機制以及雙親委派模型,雙親委派模型的類加載過程主要分為以下幾個步驟:
(1)初始化 ClassLoader 時需要指定自己的 parent 是誰
(2)先檢查類是否已經(jīng)被加載過,如果類已經(jīng)被加載了,直接返回
(3)若沒有加載則調(diào)用父加載器 parent 的 loadClass() 方法進行加載
(4)若父加載器為空則默認使用啟動類加載器 bootstrap ClassLoader 進行加載
(5)如果父類加載失敗,拋出 ClassNotFoundException 異常后,再調(diào)用自己的 findClass() 方法進行加載。
前面文章也提到,如果想要破壞這種機制,那么就自定義一個類加載器(繼承自 ClassLoader),并重寫其中的 loadClass() 方法,使其不進行雙親委派即可。最經(jīng)典例子就是 Tomcat 容器的類加載機制了,它實現(xiàn)了自己的類加載器 WebApp ClassLoader,并且打破了雙親委派模型,在每個應(yīng)用在部署后,都會創(chuàng)建一個唯一的類加載器。
1、Tomcat 的類加載器結(jié)構(gòu)圖:
(1)Common ClassLoader:加載 common.loader 屬性下的 jar,一般是 CATALINA_HOME/lib 目錄下,主要是 tomcat 使用以及應(yīng)用通用的一些類
(2)Catalina ClassLoader:加載 server.loader 屬性下的 jar,默認未配置路徑,返回其父加載器即 Common ClassLoader,主要是加載服務(wù)器內(nèi)部可⻅類,這些類應(yīng)⽤程序不能訪問;
(3)Shared Classloader:加載 share.loader 屬性下的jar,默認未配置路徑,返回其父加載器即 Common ClassLoader,主要是加載應(yīng)⽤程序共享類,這些類對 Tomcat 自己不可見;
只有指定了 tomcat/conf/catalina.properties 配置文件的 server.loader 和 share.loader 項后,才會真正建立 Catalina ClassLoader 和 Shared ClassLoader 的實例,否則在用到這兩個類加載器的地方都會用 Common ClassLoader 的實例代替,而默認的配置文件中是沒有設(shè)置這兩個 loader 項的
(4)WebApp ClassLoader:Tomcat 可以存在多個 WebApp ClassLoader 實例,每個應(yīng)⽤程序都會有⼀個獨⼀⽆⼆的 WebApp ClassLoader,⽤來加載本應(yīng)⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的類。
2、Tomcat 的類加載流程說明:
當 Tomcat 使用 WebAppClassLoader 進行類加載時,具體過程如下:
(1)先在本地 cache 緩存中查找該類是否已經(jīng)加載過,看看 Tomcat 有沒有加載過這個類
(2)如果 Tomcat 沒有加載過這個類,則從系統(tǒng)類加載器的 cache 緩存中查找是否加載過
(3)如果沒有,則使用 ExtClassLoader 類加載器類加載,重點來了,Tomcat 的 WebAppClassLoader 并沒有先使用 AppClassLoader 來加載類,而是直接使用了 ExtClassLoader 來加載類。不過 ExtClassLoader 依然遵循雙親委派,它會使用 Bootstrap ClassLoader 來對類進行加載,保證了 Jre 里面的核心類不會被重復加載。
比如在 Web 中加載一個 Object 類。WebAppClassLoader → ExtClassLoader → Bootstrap ClassLoader,這個加載鏈,就保證了 Object 不會被重復加載。
(4)如果沒有加載成功,WebAppClassLoader 就會調(diào)用自己的 findClass() 方法由自己來對類進行加載,先在 WEB-INF/classes 中加載,再從 WEB-INF/lib 中加載。
(5)如果仍然未加載成功,WebAppclassLoader 會委派給 SharedClassLoader,SharedClassLoad 再委派給 CommonClassLoader,CommonClassLoader 委派給 AppClassLoader,直到最終委派給 BootstrapClassLoader,最后再一層一層地在自己目錄下對類進行加載。
(6)都沒有加載成功的話,拋出異常。
3、源碼解析:
(1)WebAppClassLoader 的 loadClass() 方法源碼:
WebappClassLoader 應(yīng)用類加載器的 loadClass 在他的父類 WebappClassLoaderBase 中
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> clazz = null; //1. 先在本地cache查找該類是否已經(jīng)加載過 clazz = findLoadedClass0(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } //2. 從系統(tǒng)類加載器的cache中查找是否加載過 clazz = findLoadedClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } // 3. 嘗試用ExtClassLoader類加載器類加載(ExtClassLoader 遵守雙親委派,ExtClassLoader 會使用 Bootstrap ClassLoader 對類進行加載) ClassLoader javaseLoader = getJavaseClassLoader(); try { clazz = javaseLoader.loadClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } // 4. 嘗試在本地目錄搜索class并加載 try { clazz = findClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } // 5. 嘗試用系統(tǒng)類加載器(AppClassLoader)來加載 try { clazz = Class.forName(name, false, parent); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } } //6. 上述過程都加載失敗,拋出異常 throw new ClassNotFoundException(name); }
(2)WebAppClassLoader 的 findClass() 方法源碼:
public Class<?> findClass(String name) throws ClassNotFoundException { // Ask our superclass to locate this class, if possible // (throws ClassNotFoundException if it is not found) Class<?> clazz = null; // 先在自己的 Web 應(yīng)用目錄下查找 class clazz = findClassInternal(name); // 找不到 在交由父類來處理 if ((clazz == null) && hasExternalRepositories) { clazz = super.findClass(name); } if (clazz == null) { throw new ClassNotFoundException(name); } return clazz; }
4、為什么tomcat要實現(xiàn)自己的類加載機制:
WebAppClassLoader 加載類的時候,故意打破了JVM 雙親委派機制,繞開了 AppClassLoader,直接先使用 ExtClassLoader 來加載類。最主要原因是保證部署在同一個 Web 容器上的不同 Web 應(yīng)用程序所使用的類庫可以實現(xiàn)相互隔離,避免不同項目的相互影響。當然還有其他原因,如:
(1)保證 Web 容器自身的安全不受部署的 Web 應(yīng)用程序影響,所以 Tomcat 使用的類庫要與部署的應(yīng)用的類庫相互獨立
(2)保證部分基礎(chǔ)類不會被同時加載,有些類庫 Tomcat 與部署的應(yīng)用可以共享,比如說 servlet-api
(3)保證部署在同一個 Web 容器的應(yīng)用之間的類庫可以共享,這聽起來好像主要原因相互矛盾,但其實這很合理,類被類加載器加載到虛擬機后,會存放在方法區(qū)的永久代中,如果類庫不能共享,虛擬機的方法區(qū)就會很容易出現(xiàn)過度膨脹的風險。比如這時候如果有大量的應(yīng)用使用 spring 來管理,如果 spring 類庫不能共享,那每個應(yīng)用的 spring 類庫都會被加載一次,將會是很大的資源浪費。
小結(jié):Tomcat 實際上只有 WebAppClassLoader 加載器中打破了雙親委派,其他類加載器還是遵循雙親委派的。 這樣做最主要原因是保證同個 Web 容器中的不同 Web 應(yīng)用程序所使用的類庫相互獨立,避免相互影響
參考文章:https://www.jb51.net/article/229561.htm
到此這篇關(guān)于Tomcat的類加載機制流程及源碼解析的文章就介紹到這了,更多相關(guān)Tomcat類加載機制內(nèi)容請搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!
版權(quán)聲明:本站文章來源標注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。