標籤:
1. 系統載入器簡介
Java虛擬機器中可以安裝多個類載入器,系統預設三個主要類載入器(BootStrap、ExtClassLoader、AppClassLoader),每個類載入器負責載入特定位置的類。
類載入器本身也是Java類(BootStrap除外),因為它本身也要被類載入器載入,這樣顯然一定有第一個類載入器不是Java類,沒錯,正是BootStrap類載入器。它是由C++語言編寫的,嵌在了Java虛擬機器核心中的類載入器,當啟動Java虛擬機器時,它就被載入了。
2. 類載入器的結構與管轄範圍
Java虛擬機器中的所有類載入器採用具有父子關係的樹形結構進行組織。在執行個體化一個類載入器對象時都需要為其指定一個父級類載入器對象,或者預設採用系統類別載入器為其父級類載入。類載入器的樹形結構與管轄範圍如:
3. 類載入器的委託機制
當Java虛擬機器載入某一個類時,到底派出哪個類去載入呢?載入時遵循如下幾個原則:
原則1:首先派出當前線程的類載入器載入類
原則2:每個類載入器載入類時又先委託給其上級載入器。當所有的祖宗載入器沒有載入到類,才回到發起者載入器。如果還沒有載入到類,則將會拋出ClassNotFoundException。不會再去找發起者載入器的兒子,因為沒有getChild方法,即使有,那麼多個兒子(父類只有一個),找哪一個呢?
原則3:如果類A引用了類B,那麼Java虛擬機器將使用載入類A的載入器來載入類B。
原則4:還可以直接指定某個載入器來載入類,如:ClassLoader.loadClass()。
注意,每個ClassLoader本身分別只能載入特定位置和目錄中的類。但它們可以委託其它類載入器去載入類,這就是類載入器的委託模式。類載入器一級一級委託到BootStrap類載入器,當BootStrap無法載入當前所要載入的類時,然後才一級一級退回到子孫載入器去載入該類。當退回到最初的類載入器時,如果它自己也不能完成類的載入,那麼會拋出ClassNotFound異常。
4. 舉例1
首先我們定義一個空類TestClassLoader,代碼如下:
package com.tgb.ClazzLoaders;public class TestClassLoader {}
然後我們再定義一個測試類別TestMain來輸出TestClassLoader類的類載入器名稱:
package com.tgb.ClazzLoaders;public class TestMain {public static void main(String[] args) throws Exception {// 輸出類TestClassLoader的當類載入器的名稱System.out.println(TestClassLoader.class.getClassLoader().getClass().getName());}}
輸出結果如下,為sun.misc.Launcher$AppClassLoader:
然後我們將類TestClassLoader打成一個jar包放到當前使用的jre\lib\ext目錄下,如:
再次運行測試類別TestMain,我們可以驚奇的發現輸出結果變為了sun.misc.Launcher$ExtClassLoader。這正驗證了我們上面的類載入器委託機制。當載入TestClassLoader類時,當前的類載入器會向父級載入器一級一級委託,然後退回到ExtClassLoader時,它在自己的管轄範圍內jre\lib\ext\*.jar,可以找到TestClassLoader這個類。然後就將它載入了。也是就說我們運行時用到的TestClassLoader類已經不是Eclipse中我們看到的這個類了,而是jre\lib\ext目錄下我們打的TestClassLoader.jar中的類。
注意:一定要放到我們當前使用的jre目錄下,否則不起作用,如下步驟可確認,右擊項目---- >屬性--->Run/Debug Settings找到自己的configuration,然後Edit查看JRE,如:
我將jre\lib\ext目錄下我們打的TestClassLoader.jar刪除掉,然後改造測試類別,迴圈輸出類TestClassLoader的當類載入器的名稱,以及所有父類載入器的名稱。代碼如下:
package com.tgb.ClazzLoaders;public class TestMain {public static void main(String[] args) throws Exception {// 輸出類TestClassLoader的當類載入器的名稱,以及所有父類載入器的名稱ClassLoader loader = TestClassLoader.class.getClassLoader();while (loader != null) {System.out.println(loader.getClass().getName());loader = loader.getParent();}}}
結果如下:
5. 舉例2
編寫一個能列印出自己的類載入器和當前類載入器的父子結構關係鏈的MyServlet,正常發布後,看到列印結果如下:
把MyServlet檔案打Jar包,放到ext目錄中,重啟tomcat.發現找不到Httpservlet的錯誤。把servlet.jar也放到ext目錄中.問題解決了,列印的結果是ExtclassLoader。由此說明,父級類載入器載入的類無法引用只能被子級類載入器載入的類。如:
6. 總結
系統預設三個主要類載入器(BootStrap、ExtClassLoader、AppClassLoader),每個類載入器負責載入特定位置的類。Java虛擬機器中的所有類載入器採用具有父子關係的樹形結構進行組織。類載入器有一定的委託機制。此外,我們可以自訂自己的類載入器(繼承ClassLoader),然後指定類載入器的管轄範圍(載入目錄),然後我們就可以在類載入的時候對類進行一些特殊處理(例如加密)。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java類載入器