Java類加載器( 死磕 4)

【正文】Java類加載器(  CLassLoader ) 死磕 之4: 

神祕的雙親委託機制

本小節目錄

4.1. 每個類加載器都有一個parent父加載器
4.2. 類加載器之間的層次關係
4.3. 類的加載次序
4.4 雙親委託機制原理與沙箱機制
4.5. forName方法和loadClass方法的關係
4.6. 使用組合而不用繼承
4.7. 各種不同的類加載途徑


4.1.每個類加載器都有一個parent父加載器


每個類加載器都有一個parent父加載器,比如加載SystemConfig.class是由AppClassLoader完成,那麼AppClassLoader也有一個父加載器,怎麼樣獲取呢?很簡單,通過getParent方法。

這裏寫了一個公共的函數,來取得一個類的加載器的雙親樹。代碼如下:

    /**

     * 迭代,顯示class loader 和 父加載器
     */

    public static void showLoaderTree(ClassLoader loader) {

        while (loader != null) {

            Logger.info(loader.toString());

            loader = loader.getParent();

        }

    }
這個函數,不斷循環,向上顯示了父親、父親的父親、父親的父親的父親..,直到爲空。

這樣,就展示了一棵類加載器的雙親樹。

使用上面的函數,演示代碼如下:

 private static void loaderTreeDemo() throws ClassNotFoundException

    {

        String className = "com.crazymakercircle.config.SystemConfig";

        Class<?> aClass = Class.forName(className);

        ClassLoader aLoader=aClass.getClassLoader();

Logger.info("加載器:"+aLoader.toString());

        ClassLoaderUtil.showLoaderTree(aLoader);

    }



案例路徑:com.crazymakercircle.classLoaderDemo.base.ParentTreeDemo

案例提示:無編程不創客、無案例不學習。切記,一定要跑案例哦


案例結果如下:

loaderTreeDemo |>  加載器:sun.misc.Launcher$AppClassLoader@18b4aac2

  showLoaderTree |>  sun.misc.Launcher$AppClassLoader@18b4aac2

  showLoaderTree |>  sun.misc.Launcher$ExtClassLoader@6fdb1f78


這個說明,當前加載器類型爲AppClassLoader,而AppClassLoader父加載器類是ExtClassLoader。ExtClassLoader的父加載器,又是誰呢? 沒有了打印。

parent爲空表示什麼意思呢?

我們先來梳理一下加載器之間的層次關係。


4.2. 類加載器之間的層次關係


下面展示一下Bootstrap 啓動類加載器、Extention標準擴展類加載器和App應用類加載器三者之間的關係。

大致整理了如下類似的一幅圖片:

wps612B.tmp

每一個加載器看護一塊自己的地盤。 啓動Bootstrap 看護的是核心中的核心地盤。

前面講到,ExtClassLoader的父加載器爲空。而上圖中,ExtClassLoader的父加載器是Bootstrap 啓動類加載器。

實際上,如果一個加載器的parent爲空,其父親加載器就是Bootstrap 啓動類加載器。

如果沒有特別的設置,自定義加載的parent,默認爲App應用加載器。


4.3. 類的加載次序


loadClass 關鍵源代碼,已經在前面有介紹。

下面用一張圖,對於類的加載次序,做進一步的介紹。

ClassLoader 4.4.3

一般場景下,加載一個類,是從AppClassLoader開始的。

基本的步驟如下:

(1)AppClassLoader查找資源時,不是首先查看自己的地盤是否有這個字節碼文件,而是直接委託給父加載器ExtClassLoader。當然,這裏有一個假定,就是在AppClassLoader的緩存中,沒有找到目標class。比方說,第一次加載一個目標類,這個類是不會在緩存的。

(2)ExtClassLoader查找資源時,也不是首先查看自己的地盤是否有這個字節碼文件,而是直接委託給父加載器BootstrapClassLoader。

(3)如果父加載器BootstrapClassLoader在其地盤找到,並且加載成功,則直接返回了;反過來,如果在JVM的核心地盤——%sun.boot.class.path% 中沒有找到。則回到ExtClassLoader查找其地盤。

(4)如果父加載器ExtClassLoader在自己的地盤找到,並且加載成功,也直接返回了;反過來,如果在ExtClassLoader的地盤——%java.ext.dirs% 中沒有找到。則回到AppClassLoader自己的地盤。

(5)於是乎,逗了一大圈,終於回到了自己的地盤。還附帶了兩條件,就是前面的老大們沒有搞定,否則也沒有AppClassLoader啥事情了。

(6)AppClassLoader在自己的地盤找到,這個地盤就是%java.class.path%路徑下查找。找到就返回。

(7)最終,如果沒有找到,就拋出異常了。

這個過程,就是一個典型的雙親委託機制的一次執行流程。

什麼是雙親委託機制呢?


4.4. 雙親委託機制原理與沙箱機制


雙親委派模型的的原理是:

如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啓動類加載器中。只有當父加載器反饋自己無法完全這個加載請求時,子加載器纔會嘗試自己去加載。

爲什麼要使用這種雙親委託模式呢?

因爲這樣可以避免重複加載,當父親已經加載了該類的時候,就沒有必要子ClassLoader再加載一次。

雙親委託機制,也就構成了JVM 的類的沙箱機制。

沙箱機制是由基於雙親委派機制上採取的一種JVM的自我保護機制,假設你要寫一個java.lang.String 的類,由於雙親委派機制的原理,此請求會先交給Bootstrap試圖進行加載,但是Bootstrap在加載類時首先通過包和類名查找rt.jar中有沒有該類,有則優先加載rt.jar包中的類,因此就保證了java的運行機制不會被破壞。


4.5. forName方法和loadClass方法的關係


說到這裏,順便回答一下前面提出的一個問題。

前面提到,explicit顯式方式,又分兩種方式:

一是:java.lang.Class的forName()方法;

二是:java.lang.ClassLoader的loadClass()方法。

二者的區別和聯繫是什麼呢?

首先看聯繫:

Class.forName使用的是調用者的類加載器loadClass()方法來加載類的。

其次看區別。

當調用Class.forName(String)載入class時執行,會完整的完成前面提到的五步工作,就是加載、驗證、準備、解析、初始化。

如果調用ClassLoader.loadClass(String)載入class時,會執行加載、驗證、準備、解析的前面四步,並不會執行第五步——初始化。這是,類的static塊沒有被執行。需要在第一次實例化時執行,比如第一次執行 Class.newInstance() 操作時。


4.6. 使用組合而不用繼承


4.7. 各種不同的類加載途徑


Java類不是一次性加載的,而是動態被加載到內存。這是java的一大特點,也稱爲運行時綁定,或動態綁定。

除了通過Java內置的三大加載器,從JVM中系統屬性中設置的三大地盤加載Java類,還存在以下的獲取Class文件途徑: 

(1)從ZIP包中讀取。很常見,最終成爲日後JAR,WAR,EAR格式的基礎。

(2)從網絡中獲取。這種場景典型的就是Applet。

(3)運行時計算生成。典型的情景就是java動態代理技術。

(4)從其他文件中生成。典型場景是JSP應用,即由JSP文件生成對應的Class類。

(5)從不方便加入到%java.class.path%其他的文件目錄獲取。

如何實現以上的途徑呢?

具體的方法是:通自定義的類加載器,去加載其他途徑的類。



源碼:


代碼工程:  classLoaderDemo.zip

下載地址:在瘋狂創客圈QQ羣文件共享。


瘋狂創客圈:如果說Java是一個武林,這裏的聚集一羣武癡, 交流編程體驗心得
QQ羣鏈接:
瘋狂創客圈QQ羣


無編程不創客,無案例不學習。 一定記得去跑一跑案例哦


類加載器   全目錄

1.導入

2. JAVA類加載器分類

3. 揭祕ClassLoader抽象基類

4. 神祕的雙親委託機制

5. 入門案例:自定義一個文件系統的自定義classLoader

6. 基礎案例:自定義一個網絡類加載器

7. 中級案例:設計一個加密的自定義網絡加載器

8. 高級案例1:使用ASM技術,結合類加載器,解密AOP原理

9. 高級案例2:上下文加載器原理和案例

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