ClassLoader主要對類的請求提供服務,當JVM需要某類時,它根據名稱向ClassLoader要求這個類,然后由ClassLoader返回 這個類的class對象。
1.1 幾個相關概念
ClassLoader負責載入系統的所有Resources(Class,文件,來自網絡的字節流 等),通過ClassLoader從而將資源載入JVM。
每個class都有一個reference,指向自己的ClassLoader。Class.getClassLoader() 。
array的ClassLoader就是其元素的ClassLoader,若是基本數據類型,則這個array沒有ClassLoader 。
1.2 主要方法和工作過程
從JDK1.2以后,ClassLoader做了改進,使用了委托模型:所有系統中的ClassLoader組成一棵樹,ClassLoader在載入類庫時先讓Parent尋找,Parent找不到才自己找。
JVM 在運行時會產生三個ClassLoader,Bootstrap ClassLoader、Extension ClassLoader和 App ClassLoader。其中,
- Bootstrap ClassLoader是用C++編寫的,在Java中看不到它,是null。它用來加載核 心類庫,就是在lib下的類庫,
- Extension ClassLoader加載lib/ext下的類庫,
- App ClassLoader加載 Classpath里的類庫,
三者的關系為:App ClassLoader的Parent是Extension ClassLoader,而 Extension ClassLoader的Parent為Bootstrap ClassLoader。
加載一個類時,首先BootStrap進行尋 找,找不到再由Extension ClassLoader尋找,最后才是App ClassLoader。
將 ClassLoader設計成委托模型的一個重要原因是出於安全考慮,比如在Applet中,如果編寫了一個java.lang.String類並具有破 壞性。
假如不采用這種委托機制,就會將這個具有破壞性的String加載到了用戶機器上,導致破壞用戶安全。
但采用這種委托機制則不會出現這種情況。因為 要加載java.lang.String類時,系統最終會由Bootstrap進行加載,這個具有破壞性的String永遠沒有機會加載。
委托模型還帶來了一些問題,在某些情況下會產生混淆,如下是Tomcat的ClassLoader結構圖:
Bootstrap
|
System
|
Common
/
Catalina Shared
/
Webapp1 Webapp2 ...
1.4 線 程中的ClassLoader
每個運行中的線程都有一個成員contextClassLoader,用來在運行時動態地載入其它類。可以使用方法
Thread.currentThread().setContextClassLoader(...);
更改當前線程的 contextClassLoader,來改變其載入類的行為;也可以通過方法
Thread.currentThread().getContextClassLoader()
來獲得當前線程的ClassLoader。
實際上,在Java應用中所有程序都運行在線程里,如果在程序中沒有手工設置過ClassLoader,對於一般的java類如下兩種方法獲得的ClassLoader通常都是同一個
this.getClass.getClassLoader(); Thread.currentThread().getContextClassLoader();
方 法一得到的Classloader是靜態的,表明類的載入者是誰;
方法二得到的Classloader是動態的,誰執行(某個線程),就是那個執行者的 Classloader。對於單例模式的類,靜態類等,載入一次后,這個實例會被很多程序(線程)調用,對於這些類,載入的Classloader和執行 線程的Classloader通常都不同。
1.5 Web應用中的ClassLoader
回到上面的例子,在Tomcat 里,WebApp的ClassLoader的工作原理有點不同,它先試圖自己載入類(在ContextPath/WEB-INF/...中載入類),如果 無法載入,再請求父ClassLoader完成。
由此可得:
對於WEB APP線程,它的contextClassLoader是WebAppClassLoader
對於Tomcat Server線程,它的contextClassLoader是CatalinaClassLoader
1.6 獲得ClassLoader的幾種方法
可以通過如下3種方法得到ClassLoader
this.getClass.getClassLoader(); // 使用當前類的ClassLoader Thread.currentThread().getContextClassLoader(); // 使用當前線程的ClassLoader ClassLoader.getSystemClassLoader(); // 使用系統ClassLoader,即系統的入口點所使用的ClassLoader。
(注意,system ClassLoader與根 ClassLoader並不一樣。JVM下system ClassLoader通常為App ClassLoader)
2.0 資源載入
所有資源都通過ClassLoader載入到JVM里,那么在載入資源時當然可以使用ClassLoader,只是對於不同的資源還可以使用一些別的方式載 入。
例如對於類可以直接new,對於文件可以直接做IO等。
2.1 載入類的幾種方法假設有類A和類B,A在方法amethod里需要實例化B,可能的 方法有3種。對於載入類的情況,用戶需要知道B類的完整名字(包括包名,例如"com.rain.B")
1. 使用Class靜態方法 Class.forName
Class cls = Class.forName("com.rain.B"); B b = (B)cls.newInstance();
2. 使用ClassLoader
ClassLoader cl; // 如何獲得ClassLoader參考1.6 Class cls = cl.loadClass("com.rain.B"); // 使用第一步得到的ClassLoader來載入B B b = (B)cls.newInstance(); // 有B的類得到一個B的實例
3. 直接new
B b = new B();
2.2 文 件載入(例如配置文件等)
假設在com.rain.A類里想讀取文件夾 /com/rain/config 里的文件sys.properties,讀取 文件可以通過絕對路徑或相對路徑,
絕對路徑很簡單,在Windows下以盤號開始,在Unix下以"/"開始
對於相對路徑,其相對值是相對於ClassLoader的,因為ClassLoader是一棵樹,所以這個相對路徑和ClassLoader樹上的任何一個ClassLoader相對比較后可以找到文件,那么文件就可以找到,當然,讀取文件也使用委托模型
1. 直接IO
File f = new File("C:/test/com/rain/config/sys.properties"); // 使用絕對路徑 //File f = new File("com/rain/config/sys.properties"); // 使用相對路徑
InputStream is = new FileInputStream(f);
如果是配置文件,可以通過java.util.Properties.load(is)將內容讀到Properties里,Properties默認認為is的編碼是ISO-8859-1,如果配置文件是非英文的,可能出現亂碼問題。
2. 使用ClassLoader
/**
* 因為有3種方法得到ClassLoader,對應有如下3種方法讀取文件
* 使用的路徑是相對於這個ClassLoader的那個點的相對路徑,此處只能使用相對路徑
*/
InputStream is = null; is = this.getClass().getClassLoader().getResourceAsStream( "com/rain/config/sys.properties"); //方法1 is = Thread.currentThread().getContextClassLoader().getResourceAsStream( "com/rain/config/sys.properties"); //方法2 is = ClassLoader.getSystemResourceAsStream("com/rain/config/sys.properties"); //方法3
如果是配置文件,可以通過java.util.Properties.load(is)將內容讀到Properties里,這里要注意編碼問題。
3. 使用ResourceBundle
ResourceBundle bundle = ResourceBundle.getBoundle("com.rain.config.sys");
這種用法通常用來載入用戶的配置文件,關於ResourceBunlde更詳細的用法請參考其他文檔
總結:有如下3種途徑來載入文件
1. 絕對路徑 ---> IO
2. 相對路徑 ---> IO
---> ClassLoader
3. 資源文件 ---> ResourceBundle