Tomcat - 都說Tomcat違背了雙親委派機制,到底對不對?
文章目錄
類加載的本質
JVM 雙親委派機制
BootstrapClassLoader(啟動類加載器)
ExtensionClassLoader
AppClassLoader
Tomcat的 類加載順序
Tomcat要解決什么問題?
Tomcat違反了雙親委派機制?
Tomcat加載機制小結
Tomcat 熱加載JasperLoader
常見錯誤
NoClassDefFoundError
NoSuchMethodError
ClassCastException
類加載的本質
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要解決什么問題?
作為一個Web容器,Tomcat要解決什么問題 , Tomcat 如果使用默認的雙親委派類加載機制能不能行?
我們知道Tomcat可以部署多個應用,不同的應用程序可能會依賴同一個第三方類庫的不同版本,不能要求同一個類庫在同一個服務器只有一份,因此要保證每個應用程序的類庫都是獨立的,保證相互隔離 .
舉個例子 假設APP1 使用的是 Spring4 , APP2 使用的是Spring5 , 毫無疑問 Spring4 和 Spring 5 肯定有 類的全路徑一樣的類吧,如果使用雙親委派 ,父加載器加載誰?
部署在同一個web容器中相同的類庫相同的版本可以共享, 比如jdk的核心jar包,否則,如果服務器有n個應用程序,那么要有n份相同的類庫加載進虛擬機。
web容器 自己依賴的類庫 (tomcat lib目錄下),不能與應用程序的類庫混淆。基于安全考慮,應該讓容器的類庫和程序的類庫隔離開來。
web容器要支持jsp的修改, jsp 文件最終也是要編譯成class文件才能在虛擬機中運行, web容器需要支持 jsp 修改后不用重啟 ,就是熱加載的功能。
結合上面的4個問題,我們看下雙親委派能不能支持?
第一個問題,如果使用默認的類加載器機制,肯定是無法加載兩個相同類庫的不同版本的,如果使用雙親委派,讓父加載器去加載 ,不管你是什么版本的,只要你的全限定類名一樣,那肯定只有一份,APP 隔離 無法滿足
第二個問題,默認的類加載器是能夠實現的,很好理解嘛, 就是雙親委派的功能,保證唯一性。
第三個問題和第一個問題一樣。
第四個問題, 要怎么實現jsp文件的熱加載呢? jsp 文件其實也就是class文件,那么如果修改了,但類名還是一樣,類加載器會直接取方法區中已經存在的,修改后的jsp是不會重新加載的。那么怎么辦呢?可以直接卸載掉這jsp文件的類加載器 .當一個jsp文件修改了,就直接卸載這個jsp類加載器。重新創建類加載器,重新加載jsp文件。 源碼詳見: org.apache.jasper.servlet.JasperLoader
Tomcat違反了雙親委派機制?
也不盡然,核心的Java的加載還是遵從雙親委派 。
Tomcat中 各個web應用自己的類加載器(WebAppClassLoader)會優先加載,打破了雙親委派機制。加載不到時再交給commonClassLoader走雙親委托 .
原因有二
為了避免類沖突,每個 webapp 項目中各自使用的類庫要有隔離機制
不同 webapp 項目支持共享某些類庫
Tomcat加載機制小結
當tomcat啟動時,會創建幾種類加載器:
Bootstrap 引導類加載器 : 加載JVM啟動所需的類,以及標準擴展類(位于jre/lib/ext下)
System 系統類加載器 : 加載tomcat啟動的類,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于CATALINA_HOME/bin下
4. webapp 應用類加載器: 每個應用在部署后,都會創建一個唯一的類加載器。該類加載器會加載位于 WEB-INF/lib下的jar文件中的class 和 WEB-INF/classes下的class文件。
Common 通用類加載器:加載tomcat使用以及應用通用的一些類,位于CATALINA_HOME/lib下,比如servlet-api.jar
總之 當應用需要到某個類時,則會按照下面的順序進行類加載:
1 使用bootstrap引導類加載器加載 (JVM 的東西 )
2 使用system系統類加載器加載 (tomcat的啟動類Bootstrap包)
3 使用WebAppClassLoader 加載 WEB-INF/classes (應用自定義的class)
4 使用WebAppClassLoader 加載在WEB-INF/lib (應用的依賴包)
5 使用common類加載器在CATALINA_HOME/lib中加載 (tomcat的依賴包,公共的,被各個應用共享的)
Tomcat 熱加載JasperLoader
原理:后臺啟動線程監聽jsp文件變化,如果變化了找到該jsp對應的servlet類的加載器引用(gcroot),重新生成新的JasperLoader加載器賦值給引用,然后加載新的jsp對應的servlet類,之前的那個加載器因為沒有gcroot引用了,下一次gc的時候會被銷毀。
常見錯誤
NoClassDefFoundError
NoClassDefFoundError : 由于JVM或者類加載器實例嘗試加載類型的定義,但是該定義卻沒有找到,影響了執行路徑。
換句話說,在編譯時這個類是能夠被找到的,但是在執行時卻沒有找到。
NoSuchMethodError
NoSuchMethodError代表這個類型確實存在,但是一個不正確的版本被加載了。 檢查該類中是否真的有對應的方法
ClassCastException
ClassCastException,在一個類加載器的情況下,一般出現這種錯誤都會是在轉型操作時,比如:A a = (A) method();,很容易判斷出來method()方法返回的類型不是類型A,但是在 JavaEE 多個類加載器的環境下可能會出現一些不好去定位的情況。
Tomcat 容器
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。