ClassLoader 詳解及用途


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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM