1. 程式人生 > >Java類載入器( CLassLoader ) 死磕 1、2: 匯入 & 分類

Java類載入器( CLassLoader ) 死磕 1、2: 匯入 & 分類

JAVA類載入器 死磕系列 目錄 by   瘋狂創客圈

1.匯入
1.1. 從class檔案的載入開始
1.2. 什麼是類載入器
2. JAVA類載入器分類
2.1. 作業系統的環境變數
2.2. Bootstrap ClassLoader(啟動類載入器)
2.3. Extention ClassLoader (擴充套件類載入器)
2.4 Appclass Loader(應用類載入器)
2.5. 載入器啟動次序

2.6. 閱讀原始碼,檢視載入器啟動順序
2.7. 本節小結
3. 類的載入過程
3.1. 類的載入分類:隱式載入和顯示載入
3.2. 載入一個類的五步工作
3.3. 如何獲取類的載入器
3.4 解刨載入器——ClassLoader抽象基類揭祕

3.5. loadClass 關鍵原始碼分析
4 雙親委託與類的載入
4.1. 每個類載入器都有一個parent父載入器
4.2. 類載入器之間的層次關係
4.3. 類的載入次序
4.4 雙親委託機制原理與沙箱機制
4.5. forName方法和loadClass方法的關係

4.6. 使用組合而不用繼承
4.7. 各種不同的類載入途徑
5. 自定義classLoader
5.1. 自定義類載入器的基本流程
5.2. 入門案例:自定義檔案系統類載入器
5.3. 案例的環境配置
5.4 FileClassLoader 案例實現步驟
5.5. FileClassLoader 的類設計
5.6. FileClassLoader 的原始碼
5.7. FileClassLoader 的使用
5.8. 不同類載入器的名稱空間關係
5.9. 自定義載入器的兩個要點
6. 基礎案例:自定義網路類載入器

6.1. 自定義網路類載入器的類設計
6.2. 檔案傳輸Server端的原始碼
6.3. 檔案傳輸Client端的原始碼
6. 4自定義載入器SocketClassLoader的原始碼
6.5. SocketClassLoader的使用
7. 中級案例:基於加密的自定義網路載入器
7.1. 加密傳輸Server端的原始碼
7.2. 加密傳輸Client端的原始碼
7.3. 使用亦或實現簡單加密和解密演算法
7. 網路加密SafeClassLoader的原始碼
7.5. SafeSocketLoader的使用
8. 高階案例1:基於ASM,和類載入器實現AOP
8.1. ASM位元組碼操作框架簡介
8.2. ASM和訪問者模式
8.3. 用於增強位元組碼的事務類
8.4 通過ASM訪問註解
8.5. 通過ASM注入AOP事務程式碼
8.6. 實現AOP的類載入器
9. 高階案例2:上下文載入器原理和案例

9.1. 父載入器不能訪問子載入器的類
9.2. 一個寵物工廠介面
9.3. 一個寵物工廠管理類
9. 4 APPClassLoader不能訪問子載入器中的類
9.5. 執行緒上下文類載入器


【正文】Java類載入器(  CLassLoader ) 死磕 1、2: 

類的載入器匯入 & 分類


1 .1.從class檔案的載入開始

我們都知道在Java中程式是執行在虛擬機器中,我們平常用文字編輯器或者是IDE編寫的程式都是.java格式的檔案,這是最基礎的原始碼,但這類檔案是不能直接執行的。如我們編寫一個簡單的程式HelloWorld.java,Java虛擬機器並不能直接識別我們平常編寫的.java原始檔,所以需要javac這個命令轉換成.class檔案,class檔案是位元組碼格式檔案。
關鍵性的一步來了,.class如果加入JVM中呢?
這就需要用到Java的類載入器。

1.2.什麼是類載入器

ClassLoader翻譯過來就是類載入器,普通的java開發者其實用到的不多,但對於某些框架開發者來說卻非常常見。理解ClassLoader的載入機制,也有利於我們編寫出更高效的程式碼。ClassLoader的具體作用就是將class檔案載入到jvm虛擬機器中去,程式就可以正確運行了。但是,jvm啟動的時候,並不會一次性載入所有的class檔案,而是根據需要去動態載入。想想也是的,一次性載入那麼多jar包那麼多class,那記憶體不崩潰。本文的目的也是學習ClassLoader這種載入機制。

4.2.JAVA類載入器分類

講解類載入器,首先回顧一下系統的環境變數。

4.2.1.作業系統的環境變數

作業系統中,與java相關的重要的系統環境變數,你知道有哪些嗎?
初學java的時候,最害怕是配置環境變量了,關鍵是不理解,只能戰戰兢兢地照著書籍上或者是網路上的介紹進行操作。太不照顧新手了,很多菜鳥就是因為卡在環境變數的配置上,遭受了太多的挫敗感。 

java相關的環境變數的配置,主要有3個:
JAVA_HOME、PATH、CLASSPATH
具體的配置方式,與作業系統相關。 在Windows下程式設計時,可以通過系統屬性的環境變數介面,配置以上的三個環境變數。
下面介紹一下三個環境變數的含義。

一:JAVA_HOME

指的是你JDK安裝的位置,一般預設安裝在C盤,如:

C:\Program Files\Java\jdk1.8.0_131

二:PATH

PATH變數,是系統之前就已經一直存在和一直使用的。是系統指令和命令的搜尋路徑。設定PATH變數的方法是:
PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%PATH%;

在原來的PATH路徑上新增JDK目錄下的bin目錄和jre目錄的bin。將Java的bin 路徑包含在PATH當中後,在命令列視窗就可以直接鍵入Java命令的名字了,而不再需要鍵入它的全路徑。
比如編譯java原始檔的javac命令,和執行位元組碼的java命令。 如果沒有將Java的bin路徑加入到path中,執行javac命令的方法如下:
C:\Program Files\Java\jdk1.8.0_91\bin\javac  test.java

設定了設定PATH變數之後,執行javac命令的方法如下:

javac  test.java

三:CLASSPATH
CLASSPATH是指向jar包路徑和class類路徑。
一個配置的例子如下:

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

需要注意的是前面的. 點號,代表可以在當前目錄搜尋java 類檔案。

以上的三個Java 系統的環境變數,都與Java的類載入器密切相關。Java語言系統自帶有三個類載入器,具體如下圖:


4.2.2.Bootstrap ClassLoader(啟動類載入器)

Bootstrap ClassLoader是最頂層載入器,主要載入核心類庫,%java.home%\lib下的rt.jar、charsets.jar和class等。

注意,這裡的 %java.home% 不是JAVA_HOME環境變數,但是與JAVA_HOME環境變數的值相關。有很多文章介紹到這裡的時候,將 %java.home% 與JAVA_HOME搞混淆了。
專門澄清一下:%java.home%  指的是JAVA語言執行時的系統屬性java.home;JAVA_HOME環境變數,指的是JDK在作業系統中的路徑。
通過下面的語句可以獲得%java.home% 其值:

    private static void showjavahome()     {         String javahome = System.getProperty("java.home");         Logger.info("javahome = " + javahome);     }

返回的結果是:

javahome = C:\Program Files\Java\jdk1.8.0_131\jre 

對比一下,而前一個小節,我們設定的JAVA_HOME環境變數的值為:

C:\Program Files\Java\jdk1.8.0_131

所以,兩者是不一樣的。Bootstrap ClassLoader(啟動類載入器)載入的類列表清單,儲存在系統屬性%sun.boot.class.path% 中,通過獲取系統屬性的值,可以檢視。
程式碼如下:

  private static void showbootpath()     {         String path = System.getProperty("sun.boot.class.path");         Logger.info("sun.boot.class.path = " + path);     }

結果如下:

sun.boot.class.path =  C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\sunrsasign.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\classes 

通過結果可以看出,系統屬性%sun.boot.class.path%中的jar包和類路徑,都處於系統屬性%java.home% 下。

另外需要注意的是:可以通過啟動jvm時指定一個引數選項,來改變Bootstrap ClassLoader的載入目錄。這個引數選項為 -Xbootclasspath。
例如:
java -Xbootclasspath:c:/path1/jar1.jar;c:/path2/jar2.jar  Test
被指定的檔案追加到預設的bootstrap路徑中。

Bootstrap ClassLoader是由C/C++編寫的,它本身是虛擬機器的一部分,所以它並不是一個JAVA類,也就是無法在java程式碼中獲取它的引用,JVM啟動時通過Bootstrap類載入器載入rt.jar等核心jar包中的class檔案,比如int.class,String.class都是由它載入。

4.2.3.Extention ClassLoader (擴充套件類載入器)

Extention ClassLoader (擴充套件的類載入器),載入系統屬性%java.ext.dirs%目錄下的jar包和class檔案。

Extention ClassLoader (擴充套件的類載入器)載入的類列表清單,儲存在系統屬性%java.ext.dirs% 中,通過獲取該系統屬性的值,可以檢視。
程式碼如下:

  private static void showextpath()     {         String path = System.getProperty("java.ext.dirs");         Logger.info("java.ext.dirs = " + path);     }

返回的結果如下:

java.ext.dirs =
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext;
C:\WINDOWS\Sun\Java\lib\ext

另外需要注意的是:還可以載入-D java.ext.dirs選項指定的目錄。


4.2.4.Appclass Loader(應用類載入器)

與前面兩個不同,Appclass Loader 是應用程式的類載入器。Bootstrap ClassLoader 和 Extention ClassLoader, 主要是用於虛擬機器的系統類載入。
Appclass Loader(應用類載入器)的作用,載入當前應用類路徑classpath的所有類。 應用類載入器,也稱為SystemAppClass (系統應用類載入器)。Appclass Loader(應用類載入器)載入的類列表清單,儲存在系統屬性%java.class.path % 中,通過獲取該系統屬性的值,可以檢視。
程式碼如下:

 private static void showclasspath()     {         String path = System.getProperty("java.class.path");         Logger.info("java.class.path = " + path);     }

返回的結果如下:

java.class.path =  C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
D:\瘋狂創客圈 死磕Java\code\out;
D:\瘋狂創客圈 死磕Java\code\lib\cglib-3.2.3.jar;
D:\瘋狂創客圈 死磕Java\code\lib\asm-5.0.4.jar;
C:\Program Files\JetBrains\IntelliJ IDEA 2017.1.1\lib\idea_rt.jar 

回顧一下前面設定的作業系統下的環境變數CLASSPATH,如下:

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

對比之後,發現有點不對。上面輸出的系統屬性%java.class.path%的值,與配置的CLASSPATH值不一致,原因是什麼呢?
因為我們這裡使用了開發工具IntelliJ IDEA,在工程中重新配置了依賴包,相當於將系統屬性%java.class.path%的值,重新配置了一下。
具體如下圖:

4.2.5.載入器啟動次序

前面介紹了的3個類載入器,通過啟動JVM啟動Java程式的時候,怎樣知道具體哪個先行啟動呢? 
這裡,可以先揭曉答案:
1.  Bootstrap CLassLoader 
2.  Extention ClassLoader 
3.  AppClassLoader

囉嗦一句:BootstrapClassLoader、ExtClassLoader、AppClassLoader通過查閱相應的系統屬性sun.boot.class.path、java.ext.dirs和java.class.path來載入資原始檔的。


4.2.6.閱讀原始碼,檢視載入器啟動順序

三大載入器的啟動順序,可以通過閱讀原始碼來檢視。

JVM在啟動是,會執行sun.misc.Launcher 類。在該類中,建立一個Extention ClassLoader 擴充套件類載入器,和一個應用類載入器AppClassLoader。
原始碼如下:

public class Launcher {     private static Launcher launcher = new Launcher();     private static String bootClassPath =         System.getProperty("sun.boot.class.path");     private ClassLoader loader;     public Launcher() {         // 建立Extention ClassLoader 擴充套件類載入器         ClassLoader extcl;         try {             extcl = Launcher.ExtClassLoader.getExtClassLoader();         } catch (IOException e) {             throw new InternalError(                 "Could not create extension class loader", e);         }         // 建立應用類載入器AppClassLoader         try {             loader = Launcher.AppClassLoader.getAppClassLoader(extcl);         } catch (IOException e) {             throw new InternalError(                 "Could not create application class loader", e);         }    .....
}
}

在上面的程式碼中,看到了擴充套件類載入器和應用類載入器的啟動,沒有看到Bootstrap Loader的啟動。
為什麼呢?
從啟動次序來說,Bootstrap Loader作為jvm核心的核心,載入位於%java.home% 清單中的JVM 的絕對核心和基礎的類,毫無疑問,絕對是首先啟動的。由於它是基礎載入器,大多數版本jvm的bootstrap loader都不是java實現,或者不是純java的實現。理論上,Bootstrap ClassLoader 不應該叫他是一個java類。因為,它已經完全不用java實現了。Bootstrap ClassLoader是在jvm啟動時,就被構造起來的,負責java平臺核心庫。
所以,在sun.misc.Launcher 類中,看不到Bootstrap Loader的啟動。
完整的三大載入器的啟動順序如下:
1、開始執行java命令後,首先尋找jre目錄,尋找jvm.dll,並初始化JVM;
2、產生一個Bootstrap Loader(啟動類載入器);
3、建立Extended Loader(標準擴充套件類載入器);
4、建立AppClass Loader(系統類載入器),並將其父載入器(parent loader)設為Extended Loader。
這裡講到了父載入器,這是一種載入器之間的重要的關係——載入器之間的層次關係。JVM初始化sun.misc.Launcher並建立Extension ClassLoader和AppClassLoader例項。並將ExtClassLoader設定為AppClassLoader的父載入器。
Bootstrap沒有父載入器,但是實際上,它卻作為所有父載入器為空的載入器的父載入器。呵呵,捋一捋,這句話是不是很繞。

後文中,將對這種層次關係,展開進行詳細解讀。

4.2.7.本節小結

上面簡單介紹了3個ClassLoader。說明了它們載入的路徑。並且還提到了-Xbootclasspath和-D java.ext.dirs這兩個虛擬機器引數選項。 

上面也介紹了三大載入器之間的啟動順序。為後面介紹載入器之間的層次關係,打下了一個鋪墊。

本節的程式碼的類為SysPropertiesShow.java,所在的路徑為:
com.crazymakercircle.classLoaderDemo.base 


程式碼工程:  classLoaderDemo.zip

下載地址:在瘋狂創客圈QQ群檔案共享。


瘋狂創客圈:如果說Java是一個武林,這裡的聚集一群武痴, 交流程式設計體驗心得
QQ群連結:
瘋狂創客圈QQ群