深入浅出“类加载器” 之「从 sun.misc.Launcher 类源码深入探索 ClassLoader」

Posted by tomas家的小拨浪鼓 on November 7, 2019

深入浅出“类加载器” 之「从 sun.misc.Launcher 类源码深入探索 ClassLoader」

本文主要是《深入探索 JVM》系列中『类加载器』系列文章,从深入浅出“类加载器” 之「类加载机制(下)」一文中我已经通过大量的理论和示例对ClassLoader有了深入的了解。该文,我们将从 sun.misc.Launcher 源码对 ClassLoader 进行进一步的探索,也是除了示例外的另一个更本质的角度来验证我们之前说的理论。。系列文章目录见:《 深入探索 JVM 》文集


『类加载器』篇文章推荐:
深入浅出“类加载器” 之「类加载机制(上)」
深入浅出“类加载器” 之「类加载机制(下)」
深入浅出“类加载器” 之「线程上下文类加载器」
深入浅出“类加载器” 之「从 sun.misc.Launcher 类源码深入探索 ClassLoader」
深入浅出“类加载器” 之「案例分析:Tomcat 类加载器架构」


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

首先,无论是“系统类加载器”还是“扩展类加载器”都是位于 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)


返回一个给定字符串名字的类/接口相关联的 Class 对象。同时,是使用给定的类加载器对 Class 对象进行加载。给定了一个类/接口完整的全限定名的话,这个方法就会尝试的去寻找/定位、加载,并链接类或接口。那么,这个所指定的类加载器是用于加载这个指定的类或接口的。如果‘loader’参数为 null,那么这个类就会通过’启动类加载器’来进行加载。这个类只有当‘initialize’参数为 true 而且其尚未被初始化时,这个类才会被初始化。
如果参数‘name’表示的是一个“原生的类型”或者 “void”,则会将尝试在名为{@code name}的未命名包中查找用户定义的类(即,这里的说的用户定义的类就是指’原生类型’或’void类型’)。因此,这个方法是不能用于获取任何表示“原生类型”或“void”对象的。
如果参数‘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)』的区别:
    1,加载器(这时一个重要的区别):
    『public static Class forName(String className)』:使用加载了“调用该方法的类的Class类”的类加载器; 『public static Class forName(String name, boolean initialize, ClassLoader loader)』:使用指定类加载器(即,通过参数‘loader’传入的加载器)
    2,初始化
    『public static Class forName(String className)』:会对指定的类(className)进行初始化,如果该类没有被初始化过的话 『public static Class forName(String name, boolean initialize, ClassLoader loader)』:是否对指定类(name)进行初始化,由参数「initialize」决定