本文是雙親委派機制的源碼分析部分,類加載機制中的雙親委派模型對於jvm的穩定運行是很是重要的
不過源碼其實比較簡單,接下來簡單介紹一下
咱們先從啓動類提及
有一個Launcher類 sun.misc.Launcher;
仔細看下這簡短的幾行註釋,能夠獲得有用的信息
ps:直接IDE裏面查看反編譯的,看不到註釋的,能夠下載openJDK查看源碼,個人這個版本是openjdk-8-src-b132-03_mar_2014
sun.misc.Launcher 這個類是系統用於啓動主應用的啓動器 |
構造方法 Launcher() 中作了四件事情
建立 擴展 類加載器 |
建立 應用程序 類加載器 |
設置ContextClassLoader |
若是須要安裝安全管理器 security manager |
其中launcher是staitc的,因此初始化的時候就會建立對象,也就是觸發了構造方法,因此初始化的時候就會執行上面四個步驟
ExtClassLoader 和 AppClassLoader 都是Launcher的靜態內部類
並且,他們也都是ClassLoader的實現類
看下ExtClassLoader的建立中的關鍵幾步
也在看下AppClassLoader的建立中的關鍵幾步
另外還有
Launcher類中的靜態變量
你應該能夠想獲得下面這三個究竟是什麼東西,若是真不懂,你須要再去研究下
System.getProperty("sun.boot.class.path")
System.getProperty("java.ext.dirs")
System.getProperty("java.class.path")
前面說過,對於虛擬機來講只有兩種類加載器
啓動類加載器以及其餘全部,而其餘全部都是java.lang.ClassLoader的子類
因此想要自定義類加載器,必需要繼承實現ClassLoader
並且,咱們上面說到的,java給咱們提供的AppClassLoader 和 ExtClassLoader 也都是ClassLoader的子類
看下ClassLoader的構造方法 和變量parent
你會發現,其實構造方法實際上只有雙參數版本這一種
第二個參數爲parent,這個parent是一個ClassLoader, 用於記錄他的 父 類加載器
無論調用哪一個構造方法
parent必然會被初始化
要麼是你調用帶參數的構造方法, 顯式指定一個來設置parent
若是你不指定,默認的構造方法,會使用 getSystemClassLoader返回的AppClassLoader 設置parent
|
ps:
本文中的很多地方,我都在"父類加載器" 的"父 "和"類加載器"中間加了幾個空格
千萬不要理解成父類加載器 ,<父 類加載器> 指的是類加載器的加載順序層級結構的優先順序 而不是平時說的繼承關係中的父類 父 意味着他的上一層級
getSystemClassLoader 獲取AppClassLoader 的過程
|
那麼再回頭看一眼 應用程序 類加載器的構造
擴展 類加載器做爲參數傳遞給了他,他最終調用的就是ClassLoader 的一個參數的構造方法
將ExtClassLoader 設置爲 AppClassLoader 的parent
而ExtClassLoader,他的parent 是null
ps:啓動 類加載器 是虛擬機的一部分,可能c/c++/java實現的,因此不是java語言的一部分
因此對於java自己來講,能夠說他是不存在的,可是JVM是知道他的
因此說,此處爲null ,parent爲null說明他的父 類加載器是啓動類加載器 或者可能就是啓動 類加載器自己
loadClass與findClass
想要實現類 加載器,須要繼承ClassLoader
而且有兩個重要的方法
看下兩個重要方法的聲明,你可能就感受出來了,想一想public 和 protected都是啥意思?
loadClass方法是類加載器執行 加載類邏輯 的方法,包括檢查是否已經加載,調用父類加載,失敗則本身嘗試使用 findClass方法加載 |
findClass當前類加載器 實際執行加載二進制流的具體行爲方法 |
Launcher.APPClassLoader中的loadClass方法,最終調用的是super.loadClass , 實際上就是ClassLoader的loadClass方法
Launcher.ExtClassLoader 根本就沒有實現本身的loadClass 方法,因此使用的也是ClassLoader中的
|
再來看看ClassLoader的loadClass方法
他會調用parent的loadClass方法,若是他的parent不爲空,將會一直調用父 類加載器, 直到最頂級的 啓動 類加載器
若是 啓動 類加載器仍舊找尋不到, 那麼調用自身的findClass
若是本身調用findClass加載失敗呢?
很顯然, 函數調用結束以後,會返回到調用點位置,調用棧的形式嘛
也就是通過
必然要繼續執行他的下一段
若是沒拋出異常的話,就會走到下面這裏
顯然這就完成了一整個的雙親委派的類加載模式
總結
Launcher做爲啓動器
建立了ExtClassLoader 以及AppClassLoader
他們都是ClassLoader的子類,而且ClassLoader有一個parent 指向他的父 類加載器
正是這個屬性完成了自頂而下的 優先級層級順序的肯定
對於sun內置的ExtClassLoader 以及AppClassLoader 以及啓動 類加載器 Bootstrap 他們的層級爲
Bootstrap>ExtClassLoader>ExtClassLoader
而且,他們各自有不一樣的分工
經過ClassLoader的loadClass方法,肯定了他們的調用邏輯,也就是雙親委派機制
每一個層級都會向上傳遞類加載請求,只有上層 父 類加載器調用失敗,纔會本身嘗試加載
雙親委派機制的意義重大,帶來了更高的安全性等優勢
不過他的實現邏輯倒是的確很簡單
一個loadClass就搞定了
findClass是類加載器自身加載類的具體行爲
因此,若是你不須要破壞雙親委派機制,只須要覆蓋這個方法便可
若是你想要徹底自定義你的類加載器的邏輯機制,直接覆蓋loadClass,固然,你可能還須要繼續覆蓋findClass