從 sun.misc.Launcher 類源碼深入探索 ClassLoader

深入淺出“類加載器” 一文中我已經(jīng)通過大量的理論和示例對ClassLoader有了深入的了解。該文,我們將從 sun.misc.Launcher 源碼對 ClassLoader 進(jìn)行進(jìn)一步的探索,也是除了示例外的另一個(gè)更本質(zhì)的角度來驗(yàn)證我們之前說的理論。

“擴(kuò)展類加載器”和“應(yīng)用類加載器”以及“自定義類加載器”都是由“啟動(dòng)類加載器”加載的。

首先,無論是“系統(tǒng)類加載器”還是“擴(kuò)展類加載器”都是位于 sun.misc.Launcher。但是他們的訪問修飾符(default)導(dǎo)致我們在外界無法直接訪問這個(gè)加載器。

# sun.misc.Launcher 類中
static class AppClassLoader extends URLClassLoader {
……
}

static class ExtClassLoader extends URLClassLoader {
……
}

因?yàn)?AppClassLoader、ExtClassLoader 會(huì)在 Laucher 的構(gòu)造方法中被構(gòu)建;而 Launcher 的靜態(tài)屬性會(huì)去構(gòu)建一個(gè) Launcher 對象。而Launcher這個(gè)類在加載的時(shí)候會(huì)去加載static靜態(tài)塊,因此我們只需要明確Launcher這個(gè)類是由’啟動(dòng)類加載器’加載的。也就可以說明’擴(kuò)展類加載器’以及’系統(tǒng)類加載器’是由’啟動(dòng)類加載器’加載的了。

# Launcher
private static Launcher launcher = new Launcher();
public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader();    // 構(gòu)建 擴(kuò)展類加載器
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);    // 構(gòu)建 系統(tǒng)類加載器
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }

    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);
    }

}

# 加載 Launcher 的類加載器
System.out.println(Launcher.class.getClassLoader());

# 控制臺(tái)
null

??由此可見 Launcher 是由’啟動(dòng)類加載器’加載的


深入了解 ClassLoader.getSystemClassLoader() 的底層實(shí)現(xiàn)
/**
 * Returns the system class loader for delegation.  This is the default
 * delegation parent for new <tt>ClassLoader</tt> instances, and is
 * typically the class loader used to start the application.
 *
 * <p> This method is first invoked early in the runtime's startup
 * sequence, at which point it creates the system class loader and sets it
 * as the context class loader of the invoking <tt>Thread</tt>.
 *
 * <p> The default system class loader is an implementation-dependent
 * instance of this class.
 *
 * <p> If the system property "<tt>java.system.class.loader</tt>" is defined
 * when this method is first invoked then the value of that property is
 * taken to be the name of a class that will be returned as the system
 * class loader.  The class is loaded using the default system class loader
 * and must define a public constructor that takes a single parameter of
 * type <tt>ClassLoader</tt> which is used as the delegation parent.  An
 * instance is then created using this constructor with the default system
 * class loader as the parameter.  The resulting class loader is defined
 * to be the system class loader.
 *
 * <p> If a security manager is present, and the invoker's class loader is
 * not <tt>null</tt> and the invoker's class loader is not the same as or
 * an ancestor of the system class loader, then this method invokes the
 * security manager's {@link
 * SecurityManager#checkPermission(java.security.Permission)
 * <tt>checkPermission</tt>} method with a {@link
 * RuntimePermission#RuntimePermission(String)
 * <tt>RuntimePermission("getClassLoader")</tt>} permission to verify
 * access to the system class loader.  If not, a
 * <tt>SecurityException</tt> will be thrown.  </p>
 *
 * @return  The system <tt>ClassLoader</tt> for delegation, or
 *          <tt>null</tt> if none
 *
 * @throws  SecurityException
 *          If a security manager exists and its <tt>checkPermission</tt>
 *          method doesn't allow access to the system class loader.
 *
 * @throws  IllegalStateException
 *          If invoked recursively during the construction of the class
 *          loader specified by the "<tt>java.system.class.loader</tt>"
 *          property.
 *
 * @throws  Error
 *          If the system property "<tt>java.system.class.loader</tt>"
 *          is defined but the named class could not be loaded, the
 *          provider class does not define the required constructor, or an
 *          exception is thrown by that constructor when it is invoked. The
 *          underlying cause of the error can be retrieved via the
 *          {@link Throwable#getCause()} method.
 *
 * @revised  1.4
 */
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
}

返回一個(gè)基于委托模式的系統(tǒng)類加載器。它是新的類加載器默認(rèn)的委托父類實(shí)例,并且它是用于啟動(dòng)應(yīng)用的典型類加載器。
首先在運(yùn)行時(shí)的啟動(dòng)序列中調(diào)用此方法,此時(shí)它會(huì)創(chuàng)建系統(tǒng)類加載器并將其設(shè)置為調(diào)用線程的上下文類加載器。
默認(rèn)的系統(tǒng)類加載器是與這個(gè)實(shí)現(xiàn)相關(guān)的一個(gè)實(shí)例。
如果當(dāng)這個(gè)方法第一次被調(diào)用的時(shí)候,系統(tǒng)屬性”java.system.class.loader”是被定義的,那么這個(gè)屬性的值就會(huì)被作為系統(tǒng)類加載器的名字。而這個(gè)類是使用默認(rèn)的系統(tǒng)類加載器來去加載的,并且必須定義一個(gè)public的接收單個(gè)類型為ClassLoader參數(shù)的構(gòu)造方法,同時(shí)這個(gè)傳入的ClassLoader會(huì)作為委托的雙親。一個(gè)實(shí)例接下來會(huì)被創(chuàng)建通過使用這個(gè)構(gòu)造方法,同時(shí)會(huì)將默認(rèn)的系統(tǒng)類加載器作為參數(shù)傳入,而所生成的類就會(huì)被定義成’系統(tǒng)類加載器’。
也就是說,默認(rèn)的情況下’系統(tǒng)類加載器’就是’AppClassLoader’,但是對于JDK來說,如果提供了”java.system.class.loader"這個(gè)系統(tǒng)屬性,我們可以通過這個(gè)系統(tǒng)屬性來去顯示的修改“系統(tǒng)類加載器”,也就是說讓“系統(tǒng)類加載器”不再是“AppClassLoader”,而是我們自定義的某個(gè)ClassLoader。

繼續(xù)看 ClassLoader.initSystemClassLoader() 方法


『AccessController.doPrivileged(…)』: 主要是對權(quán)限的一個(gè)校驗(yàn)。你是否能這么去做,或者你是否有權(quán)限這么去做。

  • public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
public static Class<?> forName(String name, boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException
{
    Class<?> caller = null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        // Reflective call to get caller class is only needed if a security manager
        // is present.  Avoid the overhead of making this call otherwise.
        // 獲取調(diào)用當(dāng)前方法(即,forName方法)的類的 Class 對象。
        caller = Reflection.getCallerClass();
        if (sun.misc.VM.isSystemDomainLoader(loader)) {
            // 獲取’加載[調(diào)用當(dāng)前方法(即,forName方法)的類的 Class 對象]’的類加載器
            ClassLoader ccl = ClassLoader.getClassLoader(caller);
            if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                sm.checkPermission(
                    SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
        }
    }
    return forName0(name, initialize, loader, caller);
}

/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
                                        ClassLoader loader,
                                        Class<?> caller)
    throws ClassNotFoundException;

返回一個(gè)給定字符串名字的類/接口相關(guān)聯(lián)的 Class 對象。同時(shí),是使用給定的類加載器對 Class 對象進(jìn)行加載。給定了一個(gè)類/接口完整的全限定名的話,這個(gè)方法就會(huì)嘗試的去尋找/定位、加載,并鏈接類或接口。那么,這個(gè)所指定的類加載器是用于加載這個(gè)指定的類或接口的。如果‘loader’參數(shù)為 null,那么這個(gè)類就會(huì)通過’啟動(dòng)類加載器’來進(jìn)行加載。這個(gè)類只有當(dāng)‘initialize’參數(shù)為 true 而且其尚未被初始化時(shí),這個(gè)類才會(huì)被初始化。
如果參數(shù)‘name’表示的是一個(gè)“原生的類型”或者 “void”,則將嘗試在名為{@code name}的未命名包中查找用戶定義的類。因此,這個(gè)方法是不能用于獲取任何表示“原生類型”或“void”的 Class 對象。
如果參數(shù)‘name’表示的是一個(gè)數(shù)組類,那么數(shù)組的’component type’就會(huì)被加載,但不會(huì)被初始化。

example:

* <blockquote>
*  {@code Class.forName("Foo")}
* </blockquote>
*
* is equivalent to:
*
* <blockquote>
*  {@code Class.forName("Foo", true, this.getClass().getClassLoader())}
* </blockquote>

注意,該方法并不會(huì)檢測所請求的類對于其調(diào)用者來說是否可訪問。
參數(shù):
a)name ———— 指定類的完整限定名
b)initialize ———— 是否初始化
c)loader ———— 用于加載指定類的類加載器

# public static Class<?> forName(String className)
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    // 這里的 classLoader 是使用了:加載了“調(diào)用該方法的類的Class類”的類加載器。
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
  • 『public static Class<?> forName(String className)』與『public static Class<?> forName(String name, boolean initialize, ClassLoader loader)』的一個(gè)重要區(qū)別:
    『public static Class<?> forName(String className)』使用加載了“調(diào)用該方法的類的Class類”的類加載器;
    『public static Class<?> forName(String name, boolean initialize, ClassLoader loader)』:使用指定類加載器(即,通過參數(shù)‘loader’傳入的加載器)

相關(guān)文章

深入淺出“類加載器”
深入探索“線程上下文類加載器”
ClassLoader 源碼詳解

參考

圣思園《深入理解JVM》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 作者:成 富, 軟件工程師, IBM 中國軟件開發(fā)中心 類加載器(class loader)是 Java?中的一個(gè)...
    Android技術(shù)研究閱讀 3,984評論 0 74
  • ClassLoader翻譯過來就是類加載器,普通的java開發(fā)者其實(shí)用到的不多,但對于某些框架開發(fā)者來說卻非常常見...
    時(shí)待吾閱讀 1,168評論 0 1
  • 類的加載指的是將類的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,將其放在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在堆區(qū)創(chuàng)建一個(gè)...
    dinel閱讀 461評論 0 0
  • 類的生命周期 類加載過程包括:加載-驗(yàn)證-準(zhǔn)備-解析-初始化。這個(gè)過程順序并不是固定的,最多僅僅代表它們開始的順序...
    jection閱讀 470評論 0 1
  • 臨近國慶,又有一大批好看的影片將要被搬上屏幕,這其中,便有國師張藝謀的新作《影》。 《長城》之后,他帶著自己對“武...
    海角七號地閱讀 1,456評論 5 19

友情鏈接更多精彩內(nèi)容