Tomcat - 都說Tomcat違背了雙親委派機制,到底對不對?


在這裏插入圖片描述


類加載的本質

ClassLoader是用來加載 Class 的。它負責將 Class 的字節碼形式轉換成內存形式的 Class 對象。

字節碼可以來自於磁盤文件 *.class,也可以是 jar 包裏的 *.class,也可以來自遠程服務器提供的字節流,字節碼的本質就是一個字節數組 []byte,它有特定的複雜的內部格式。

JVM 運行實例中會存在多個 ClassLoader,不同的 ClassLoader 會從不同的地方加載字節碼文件。

它可以從不同的文件目錄加載,也可以從不同的 jar 文件中加載,也可以從網絡上不同的靜態文件服務器來下載字節碼再加載。


JVM 雙親委派機制

在這裏插入圖片描述

Java1.2之後引入雙親委派模式 。

核心原理: 如果其中一個類加載器收到了類加載的請求,它並不會自己去加載而是會將該請求委託給父類的加載器去執行,如果父類加載器還存在父類加載器,則進一步向上委託,如此遞歸,請求最終到達頂層的啓動類加載器。

如果父類能加載,則直接返回,如果父類加載不了則交由子類加載,這就是雙親委派模式。

在這裏插入圖片描述

好處

  • 向上委託給父類加載,父類加載不了再自己加載
  • 避免重複加載,防止Java核心api被篡改

BootstrapClassLoader(啓動類加載器)

負責加載 JVM 運行時核心類,加載System.getProperty("sun.boot.class.path")所指定的路徑或jar


ExtensionClassLoader

負責加載 JVM 擴展類,比如 swing 系列、內置的 js 引擎、xml 解析器 等等,這些庫名通常以 javax 開頭,它們的 jar 包位於 JAVAHOME/lib/rt.jar文件中.
加載System.getProperty("java.ext.dirs")所指定的路徑或jar。

在使用Java運行程序時,也可以指定其搜索路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld


AppClassLoader

AppClassLoader纔是直接面向我們用戶的加載器,它會加載 Classpath 環境變量裏定義的路徑中的 jar 包和目錄。

我們自己編寫的代碼以及使用的第三方 jar 包通常都是由它來加載的。

加載System.getProperty("java.class.path")所指定的路徑或jar。

在使用Java運行程序時,也可以加上-cp來覆蓋原有的Classpath設置,例如: java -cp ./lavasoft/classes HelloWorld


Tomcat的 類加載順序

在這裏插入圖片描述

在Tomcat中,默認的行爲是先嚐試在Bootstrap和Extension中進行類型加載,如果加載不到則在WebappClassLoader中進行加載,如果還是找不到則在Common中進行查找 .

Common是公共的包, tomcat可以外掛很多個webapps (tomcat和app分開部署) ,優先以webapps下的爲主。

tomcat7 —> 默認 WebappClassLoader 類加載器
tomcat 8 ---->默認 ParallelWebappClassLoader 類加載器


Tomcat違反了雙親委派機制?

也不盡然,核心的Java的加載還是遵從雙親委派 。

Tomcat中 各個web應用自己的類加載器(WebAppClassLoader)會優先加載,加載不到時再交給commonClassLoader走雙親委託 .

原因有二

  • 爲了避免類衝突,每個 webapp 項目中各自使用的類庫要有隔離機制
  • 不同 webapp 項目支持共享某些類庫

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中加載


常見錯誤

NoClassDefFoundError

NoClassDefFoundError是在開發JavaEE程序中常見的一種問題。

該問題會隨着你所使用的JavaEE中間件環境的複雜度以及應用本身的體量變得更加複雜,尤其是現在的JavaEE服務器具有大量的類加載器。

在JavaDoc中對NoClassDefFoundError的產生是由於JVM或者類加載器實例嘗試加載類型的定義,但是該定義卻沒有找到,影響了執行路徑。

換句話說,在編譯時這個類是能夠被找到的,但是在執行時卻沒有找到。
這一刻IDE是沒有出錯提醒的,但是在運行時卻出現了錯誤。


NoSuchMethodError

在另一個場景中,我們可能遇到了另一個錯誤 NoSuchMethodError
NoSuchMethodError代表這個類型確實存在,但是一個不正確的版本被加載了。


ClassCastException

ClassCastException,在一個類加載器的情況下,一般出現這種錯誤都會是在轉型操作時,比如:A a = (A) method();,很容易判斷出來method()方法返回的類型不是類型A,但是在 JavaEE 多個類加載器的環境下就會出現一些難以定位的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章