ClassLoader雙親委派機制源碼分析

前言:
為了更好的分析Tomcat的類加載機制,首先要對JDK的類加載機制有較為深入的理解,JDK類加載機制的前期理論在http://www.lxweimin.com/p/639f430fe15a中已有闡述,故這里只從源碼的角度看待雙親委派機制的代碼實現,本文主要驗證和解惑的問題是

  • 引導類加載器BootStrap ClassLoader、擴展類加載器Extension ClassLoader、應用類加載器Application ClassLoader(或者稱為系統類加載器System ClassLoader)加載的內容(哪些文件夾)在源碼中是如何體現的
  • 類加載器父子關系在源碼中如何體現
  • 雙親委派機制的整個過程在源碼中如何實現

擴展類加載器Extension ClassLoader和應用類加載器Application ClassLoader對應的都是rt.jar下sun.misc.Launcher.class中的兩個靜態內部類ExtClassLoaderAppClassLoader,兩個類以組合的形式存在于Launcher類中;而引導類加載器BootStrap ClassLoader因為是使用C++實現,因此jdk中沒有特定的類與之對應,我們可以通過查閱openjdk源碼和jdk代碼推理的手段理解它,本文使用第二種方式。jdk中類加載相關類圖如下所示

JDK類加載器相關類UML圖

1. 引導類加載器、擴展類加載器和應用類加載器各自加載哪些內容

引導類加載器加載路徑為sun.boot.class.path

BootStrap ClassLoader加載路徑

擴展類加載器加載路徑為java.ext.dirs,比如ext目錄下

Extension ClassLoader加載路徑

應用類加載器加載路徑為java.class.path,即classpath

Application ClassLoader加載路徑

2. 類加載之間的父子關系如何體現

對于該問題的回答,我們需要從sum.misc.Lanucher.class初始化時進行分析

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            //                (1)初始化擴展類加載器
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader");
        }

        try {
            //                (2)初始化應用類加載器
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader");
        }
        //                    (3)線程上下文加載器
        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if(var2 != null) {
            SecurityManager var3 = null;
            if(!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                    ;
                } catch (InstantiationException var6) {
                    ;
                } catch (ClassNotFoundException var7) {
                    ;
                } catch (ClassCastException var8) {
                    ;
                }
            } else {
                var3 = new SecurityManager();
            }

            if(var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

(1)處注釋初始化了擴展類加載器,而真正的調用代碼在靜態內部類ExtClassLoader.class中的getExtClassLoader()中,我們接著看

ExtClassLoader初始化過程解析

getExtClassLoader()內部通過調用該類的構造器,返回了一個ExtClassLoader的實例,這里構造器傳入的參數var0實際上就是ExtClassLoader加載目錄的文件對象。第二個紅線說明創建ExtClassLoader的實例實際上還得調用其父類URLClassLoader的構造器,其中有三個參數,第二個參數最為重要,該參數表示ExtClassLoader對象的父加載器是誰

URLClassLoader構造器

這里URLClassLoader會再調用其父類的構造器,最終止于ClassLoader,這里我們無需關心,現在需要牢記的是ExtClassLoader.class的父類加載器為null
我們接著來看代碼的第二個注釋初始化應用類加載器,源碼中將(1)處創建的ExtClassLoader的實例var1傳遞給了,應用類加載器的getAppClassLoader()方法,聰明的讀者在這里實際上可以推斷出,該var1所指代的ExtClassLoader必定就是通過該方法被設置成了Application ClassLoader的父類加載器,bingo

Application ClassLoader初始化源碼

從上圖中得知,Application ClassLoader也走了ExtClassLoader的老路,那么這里的var2就是parent擴展類加載器ExtClassLoader了。至此ClassLoader父子關系在JDK源碼中是如何做的也就清楚了,但是之前留的問題null為什么會是ExtClassLoader的parent還是沒有解決,下面第三個問題的解釋中隱藏了該問題的答案。

3. 委派機制如何實現

由于類加載器的雙親委派機制,因此,我們可以通過任何一個類加載器加載類的代碼來梳理整個類加載器的加載流程,這里我們選擇應用類加載器Application ClassLoader,雙親委派機制的具體邏輯存在于類加載器的loadClass方法中

Application ClassLoader的loadClass方法

紅線處代碼調用Application ClassLoader父類的loadClass方法,而由第一張UML圖可知,Application ClassLoader的父類為URLClassLoader和ClassLoader,繼續追蹤

ClassLoader進行類加載的核心方法

紅框內的代碼即是實現委派機制的代碼,邏輯很簡單:如果當前類加載器的父類加載器不為空,就先讓副類加載器加載name所對應的類,這里的parent成員變量實際上就是第二個問題中類加載器初始化時傳遞的父類加載器,這就解釋了ExtClassLoader的父類加載器傳遞的是null,就會執行else的邏輯,調用findBootstrapClassOrNull(),而該方法最終為native方法private native Class findBootstrapClass(String name);,實際上就是調用openjdk中BootStrap ClassLoader的實現去加載該類

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容