Java 系统 Class 加载程序并未真正被替换

Java System Class Loader is not really being replaced

Java系统classLoader真的可以被取代吗?

Java 有一个名为 -Djava.system.class.loader 的 属性 用于替换系统 class 加载器。

但是,使用 new 而不是 .newInstance() 创建的任何 class 都显示 AppClassLoader 作为其 class 加载器。

AppClassLoader 也称为系统 class 加载程序,因此似乎没有任何真正的替换正在进行。

例如,当 运行(Oracle Hotspot on Windows JDK 1.8)

// Run with java -Djava.system.class.loader=classLoaders.MyClassLoader Example

public class MyClassLoader extends ClassLoader {
    public MyClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    public Class loadClass(String name) throws ClassNotFoundException {
        System.out.println("Loading class :" + name);
        return super.loadClass(name);
    }
}

class A {
}

public class Example {
    public static void main(String[] args) throws Exception {
        System.out.println(ClassLoader.getSystemClassLoader());
        System.out.println(new Example().getClass().getClassLoader());
        System.out.println(new A().getClass().getClassLoader());
    }
}

输出为:

Loading class :classLoaders.Example
classLoaders.MyClassLoader@6d06d69c
sun.misc.Launcher$AppClassLoader@58644d46
sun.misc.Launcher$AppClassLoader@58644d46

虽然 Example 的第一个实例正在被 MyClassLoader 和方法 getSystemClassLoader returns MyClassLoader 加载,但任何 class 使用 new 创建的是由原始系统 Class Loader 加载的,那么究竟被替换了什么?

您应该重写 MyClassLoader 的 toString() 方法,看看它是否有效。

在 JVM 的早期引导序列中,class 加载程序必须就位以加载 class 覆盖 class 加载程序。为此,还必须加载许多其他 classes,以实现原始 class 加载程序并构建足够的 JVM 以正确接受覆盖的 class 加载程序。

在 class 加载程序被成功覆盖后,它应该为 class 的其余部分调用您的覆盖。

这意味着您必须注意不要将对未实现版本的早期 class 加载程序调用视为错误。

您只是在扩展 ClassLoader 并使用 super.loadClass(); 因此,其他一些真正的 class 加载程序正在执行实际的 class 加载。

在幕后,系统似乎将加载工作委托给系统 class 加载程序。

这里已经回答了同样的问题:

  1. MyClassLoader 是启动 class 加载程序
  2. AppClassLoader 是定义 class 加载程序
  3. 同一个包中加载的所有 classes 都来自定义 class 加载器(它实际上加载了包中的第一个 class)。

A class loader L may create C by defining it directly or by delegating to another class loader. If L creates C directly, we say that L defines C or, equivalently, that L is the defining loader of C.

When one class loader delegates to another class loader, the loader that initiates the loading is not necessarily the same loader that completes the loading and defines the class. If L creates C, either by defining it directly or by delegation, we say that L initiates loading of C or, equivalently, that L is an initiating loader of C.

At run time, a class or interface is determined not by its name alone, but by a pair: its binary name (§4.2.1) and its defining class loader. Each such class or interface belongs to a single run-time package. The run-time package of a class or interface is determined by the package name and defining class loader of the class or interface.

虽然 MyClassLoader 调用 super.loadClass,但 ClassLoader->loadClass 的实现只找到另一个实例,并委托给它。

因此,父 class 实现仅将作业传递给不同的实例。