从 sun.misc.Launcher 类源码深入探索 ClassLoader

深入浅出“类加载器” 一文中我已经通过大量的理论和示例对ClassLoader有了深入的了解。该文,我们将从 sun.misc.Launcher 源码对 ClassLoader 进行进一步的探索,也是除了示例外的另一个更本质的角度来验证我们之前说的理论。

“扩展类加载器”和“应用类加载器”以及“自定义类加载器”都是由“启动类加载器”加载的。

首先,无论是“系统类加载器”还是“扩展类加载器”都是位于 sun.misc.Launcher。但是他们的访问修饰符(default)导致我们在外界无法直接访问这个加载器。

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

static class ExtClassLoader extends URLClassLoader {
……
}

因为 AppClassLoader、ExtClassLoader 会在 Laucher 的构造方法中被构建;而 Launcher 的静态属性会去构建一个 Launcher 对象。而Launcher这个类在加载的时候会去加载static静态块,因此我们只需要明确Launcher这个类是由’启动类加载器’加载的。也就可以说明’扩展类加载器’以及’系统类加载器’是由’启动类加载器’加载的了。

# Launcher
private static Launcher launcher = new Launcher();
public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader();    // 构建 扩展类加载器
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);    // 构建 系统类加载器
    } 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());

# 控制台
null

👆由此可见 Launcher 是由’启动类加载器’加载的


深入了解 ClassLoader.getSystemClassLoader() 的底层实现
/**
 * 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;
}

返回一个基于委托模式的系统类加载器。它是新的类加载器默认的委托父类实例,并且它是用于启动应用的典型类加载器。
首先在运行时的启动序列中调用此方法,此时它会创建系统类加载器并将其设置为调用线程的上下文类加载器。
默认的系统类加载器是与这个实现相关的一个实例。
如果当这个方法第一次被调用的时候,系统属性”java.system.class.loader”是被定义的,那么这个属性的值就会被作为系统类加载器的名字。而这个类是使用默认的系统类加载器来去加载的,并且必须定义一个public的接收单个类型为ClassLoader参数的构造方法,同时这个传入的ClassLoader会作为委托的双亲。一个实例接下来会被创建通过使用这个构造方法,同时会将默认的系统类加载器作为参数传入,而所生成的类就会被定义成’系统类加载器’。
也就是说,默认的情况下’系统类加载器’就是’AppClassLoader’,但是对于JDK来说,如果提供了”java.system.class.loader"这个系统属性,我们可以通过这个系统属性来去显示的修改“系统类加载器”,也就是说让“系统类加载器”不再是“AppClassLoader”,而是我们自定义的某个ClassLoader。

继续看 ClassLoader.initSystemClassLoader() 方法


『AccessController.doPrivileged(…)』: 主要是对权限的一个校验。你是否能这么去做,或者你是否有权限这么去做。

  • 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.
        // 获取调用当前方法(即,forName方法)的类的 Class 对象。
        caller = Reflection.getCallerClass();
        if (sun.misc.VM.isSystemDomainLoader(loader)) {
            // 获取’加载[调用当前方法(即,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;

返回一个给定字符串名字的类/接口相关联的 Class 对象。同时,是使用给定的类加载器对 Class 对象进行加载。给定了一个类/接口完整的全限定名的话,这个方法就会尝试的去寻找/定位、加载,并链接类或接口。那么,这个所指定的类加载器是用于加载这个指定的类或接口的。如果‘loader’参数为 null,那么这个类就会通过’启动类加载器’来进行加载。这个类只有当‘initialize’参数为 true 而且其尚未被初始化时,这个类才会被初始化。
如果参数‘name’表示的是一个“原生的类型”或者 “void”,则将尝试在名为{@code name}的未命名包中查找用户定义的类。因此,这个方法是不能用于获取任何表示“原生类型”或“void”的 Class 对象。
如果参数‘name’表示的是一个数组类,那么数组的’component type’就会被加载,但不会被初始化。

example:

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

注意,该方法并不会检测所请求的类对于其调用者来说是否可访问。
参数:
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 是使用了:加载了“调用该方法的类的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)』的一个重要区别:
    『public static Class<?> forName(String className)』使用加载了“调用该方法的类的Class类”的类加载器;
    『public static Class<?> forName(String name, boolean initialize, ClassLoader loader)』:使用指定类加载器(即,通过参数‘loader’传入的加载器)

相关文章

深入浅出“类加载器”
深入探索“线程上下文类加载器”
ClassLoader 源码详解

参考

圣思园《深入理解JVM》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 作者:成 富, 软件工程师, IBM 中国软件开发中心 类加载器(class loader)是 Java™中的一个...
    Android技术研究阅读 3,878评论 0 74
  • ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见...
    时待吾阅读 1,028评论 0 1
  • 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个...
    dinel阅读 412评论 0 0
  • 类的生命周期 类加载过程包括:加载-验证-准备-解析-初始化。这个过程顺序并不是固定的,最多仅仅代表它们开始的顺序...
    jection阅读 380评论 0 1
  • 临近国庆,又有一大批好看的影片将要被搬上屏幕,这其中,便有国师张艺谋的新作《影》。 《长城》之后,他带着自己对“武...
    海角七号地阅读 1,239评论 5 19