类加载器

Wu Jun 2020-01-03 17:43:49
Categories: > > Tags:

1 类加载过程

虚拟机只加载程序执行时所需要的类文件。假设程序从 MyProgram.class 开始运行,下面是虚拟机执行的步骤:

  1. 虚拟机有一个用于加载类文件的机制。
  2. 如果类拥有类型为另一个类的域,或者是拥有超类,那么这些类文件也会被加载。
  3. 接着,虚拟机执行类中的 main 方法。
  4. 如果 main 方法或者 main 调用的方法要用到更多的类,那么接下来就会加载这些类。

每个Java程序至少拥有三个类加载器:

2 类加载器的层次结构

类加载器有一种父/子关系。除了引导类加载器外,每个类加载器都有一个父类加载器。父类加载失败子类才加载。

如果插件被打包为 JAR 文件,那就可以直接用 URLClassLoader 类的实例去加载这些类。

URL url = new URL("path");
URLClassLoader pluginLoader = new URLClassLoader(new URL[] {url});
Class<?> cl = pluginLoader.loadClass("mypackage.MyClass");

可以通过下面将其设置成为任何类加载器

Thread = Thread.currentThread();
t.setContextClassLoader(loader);

助手方法可以获取这个上下文类加载器:

Thread t = Thread.currentThread();
ClassLoader loader = t.getCOntextClassLoader();
Class cl = loader.loaderClass(className);

当调用由不同的类加载器加载的插件类的方法时,进行上下文类加载器的设置是一种好的思路;或者,让助手方法的调用者设置上下文类加载器。

3 将类加载器作为命名空间

同一虚拟机中,可以有两个类的类名和包名都相同。类是由它的全名和类加载器来确定的。

4 编写自己的类加载器

编写自己的类加载器,只需要继承 ClassLoader 类,然后覆盖下面这个方法

findClass(String className)

ClassLoader 超类的 loadClass 方法用于将类的加载操作委托给其父类加载器去进行,只有当该类尚未加载并且父类加载器也无法加载该类时,才调用 findClass 方法。

如果要实现该方法,必须做到以下几点:

  1. 为来自本地文件系统或者其他来源的类加载其字节码
  2. 调用 ClassLoader 超类的 defineClass 方法,想虚拟机提供字节码。

5 字节码校验

当类加载器将新在加载的 Java 平台类的字节码传递给虚拟机,这些字节码首先要接受校验器的校验。校验器负责检查那些指令无法执行的明细那有破坏性的操作。出了系统类外,所有的类都要被校验。

下面是校验器执行的一些检查: