第5講 深入理解 ClassLoader 的加載機制

第05講 深入理解 ClassLoader 的加載機制

拉勾教育:https://kaiwu.lagou.com/course/courseInfo.htm

這一講主要講了class文件的加載時機,及加載class的雙親委派機制。還舉例說明了如何自定義classLoader。最後還介紹了android中的類加載器,並舉例使用自定義的類加載器進行熱修復。下面來一一看一下這些內容。

一、class文件的加載時機

在 Java 程序啓動的時候,並不會一次性加載程序中所有的 .class 文件,而是在程序的運行過程中,動態地加載相應的類到內存中。

通常情況下,Java 程序中的 .class 文件會在以下 2 種情況下被 ClassLoader 主動加載到內存中:

  1. 調用類構造器
  2. 調用類中的靜態(static)變量或者靜態方法

二、雙親委派模型

2.1 類加載器種類

從開發者的角度,類加載器可以細分爲:

  1. 啓動類加載器(BootstrapClassLoader):負責將 Java_Home/lib下面的類庫加載到內存中(比如rt.jar)。由於引導類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啓動類加載器的引用,所以不允許直接通過引用進行操作。

  2. 標準擴展類加載器(ExtClassLoader ,JDK 1.9 之後,改名爲 PlatformClassLoader):是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實現的。它負責將Java_Home /lib/ext或者由系統變量 java.ext.dir指定位置中的類庫加載到內存中。開發者可以直接使用標準擴展類加載器。

  3. 應用程序類加載器(APPClassLoader):是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的。它負責將系統類路徑(CLASSPATH)中指定的類庫加載到內存中。開發者可以直接使用系統類加載器。由於這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般稱爲系統(System)加載器。

除此之外,還有自定義的類加載器,它們之間的關係通過被稱爲類加載器的雙親委派模型來體現。

2.2 雙親委派模型

用一張圖來表示各個類加載器之間的委派關係
在這裏插入圖片描述

雙親委派模型即某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。

使用雙親委派模型的好處在於Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係。例如類java.lang.Object,它存在在rt.jar中,無論哪一個類加載器要加載這個類,最終都是委派給處於模型最頂端的Bootstrap ClassLoader進行加載,因此Object類在程序的各種類加載器環境中都是同一個類。相反,如果沒有雙親委派模型而是由各個類加載器自行加載的話,如果用戶編寫了一個java.lang.Object的同名類並放在ClassPath中,那系統中將會出現多個不同的Object類,程序將混亂。而在雙親委派模型,這個自已編寫的Object類可以正常編譯,但是永遠無法被加載運行。

雙親委派模型在代碼中的實現

protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
    //check the class has been loaded or not
    Class c = findLoadedClass(name);
    if (c == null) {
           try {
               if (parent != null) {
                   c = parent.loadClass(name, false);
               } else {
                   c = findBootstrapClassOrNull(name);
               }
           } catch (ClassNotFoundException e) {
               // ClassNotFoundException thrown if class not found
               // from the non-null parent class loader
           }

           if (c == null) {
               // If still not found, then invoke findClass in order
               // to find the class.
               c = findClass(name);
           }
       }
    return c;
}
  • 注意:“雙親委派”機制只是 Java 推薦的機制,並不是強制的機制。我們可以繼承 java.lang.ClassLoader 類,實現自己的類加載器。
  • 如果想保持雙親委派模型,就應該重寫 findClass(name) 方法;
  • 如果想破壞雙親委派模型,可以重寫 loadClass(name) 方法。

三、自定義classLoader

JVM 中預置的 3 種 ClassLoader 只能加載特定目錄下的 .class 文件,如果我們想加載其他特殊位置下的 jar 包或類時(比如,我要加載網絡或者磁盤上的一個 .class 文件),默認的 ClassLoader 就不能滿足我們的需求了,所以需要定義自己的 Classloader 來加載特定目錄下的 .class 文件。

自定義 ClassLoader 步驟

  1. 自定義一個類繼承抽象類 ClassLoader。
  2. 重寫 findClass 方法。
  3. 在 findClass 中,調用 defineClass 方法將字節碼轉換成 Class 對象,並返回。

課程舉的例子,看下它的代碼:
在這裏插入圖片描述

注意:上述動態加載 .class 文件的思路,經常被用作熱修復和插件化開發的框架中,包括 QQ 空間熱修復方案、微信 Tink 等原理都是由此而來。客戶端只要從服務端下載一個加密的 .class 文件,然後在本地通過事先定義好的加密方式進行解密,最後再使用自定義 ClassLoader 動態加載解密後的 .class 文件,並動態調用相應的方法。

四、android中的類加載器

本質上,Android 和傳統的 JVM 是一樣的,也需要通過 ClassLoader 將目標類加載到內存,類加載器之間也符合雙親委派模型。但是在 Android 中, ClassLoader 的加載細節有略微的差別。

在 Android 虛擬機裏是無法直接運行 .class 文件的,Android 會將所有的 .class 文件轉換成一個 .dex 文件,並且 Android 將加載 .dex 文件的實現封裝在 BaseDexClassLoader 中,而我們一般只使用它的兩個子類:PathClassLoader 和 DexClassLoader。

PathClassLoader

PathClassLoader 用來加載系統 apk 和被安裝到手機中的 apk 內的 dex 文件。當一個 App 被安裝到手機後,apk 裏面的 class.dex 中的 class 均是通過 PathClassLoader 來加載的

DexClassLoader

先來看官方對 DexClassLoader 的描述:

A class loader that loads classes from .jar and .apk filescontaining a classes.dex entry.

This can be used to execute code notinstalled as part of an application.

很明顯,對比 PathClassLoader 只能加載已經安裝應用的 dex 或 apk 文件,DexClassLoader 則沒有此限制,可以從 SD 卡上加載包含 class.dex 的 .jar 和 .apk 文件,這也是插件化和熱修復的基礎,在不需要安裝應用的情況下,完成需要使用的 dex 的加載。




由於水平有限,如果文中存在錯誤之處,請大家批評指正,歡迎大家一起來分享、探討!

博客:http://blog.csdn.net/MingHuang2017

GitHub:https://github.com/MingHuang1024

Email: [email protected]

g2017)

GitHub:https://github.com/MingHuang1024

Email: [email protected]

微信:724360018

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