背景知识

在介绍双亲委派机制之前,首先需要了解 ClassLoader(类加载器) 的相关概念。

Java 程序运行在 Java 虚拟机(JVM)之中。我们在 IDE 中编写的 Java 源代码被编译器编译成 .class 字节码文件,随后由 ClassLoader 负责将这些 class 文件加载到 JVM 中执行。

JVM 中主要提供了三层 ClassLoader:

  • Bootstrap ClassLoader:主要负责加载核心类库(如 java.lang.* 等),并负责构造 ExtClassLoader 和 AppClassLoader。
  • ExtClassLoader:主要负责加载 jre/lib/ext 目录下的一些扩展 jar 包。
  • AppClassLoader:主要负责加载应用程序的主函数类及classpath 下的类。

那么,一个由 Hello.java 编译成的 Hello.class 文件,究竟是如何被加载到 JVM 中的呢?

双亲委派机制详解

查看 JDK 源码中 java.lang 包下的 ClassLoader 类,重点关注其 loadClass 方法,核心逻辑如下:

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

// ----- 核心逻辑 -----
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // 首先,检查是否已经被类加载器加载过
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            // 存在父加载器,递归地交由父加载器处理
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                // 直到最上面的 Bootstrap 类加载器
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 如果父加载器无法找到该类,则捕获异常
            // ClassNotFoundException thrown if class not found
            // from the non-null parent class loader
        }

        if (c == null) {
            // 如果仍未找到,则调用 findClass 方法尝试自己加载
            c = findClass(name);
        }
    }
    return c;
}

这段代码清晰地解释了双亲委派机制。为了更直观地理解,我们可以通过下图来描述上述代码的执行流程:

20201217213314510.png

结合上图与源码,当一个类(如 Hello.class)需要被加载时,流程如下:

  1. 检查是否已加载:首先会在当前的 ClassLoader(例如 AppClassLoader)中检查该类是否已经被加载过,如果有则无需再次加载。
  2. 委托父加载器:如果没有加载过,则获取父加载器,调用父加载器的 loadClass 方法。
  3. 递归向上:父加载器同理,先检查自己是否加载过,如果没有则继续向上委托。这是一个类似递归的过程,直到到达 Bootstrap ClassLoader
  4. 向下查找:在到达 Bootstrap ClassLoader 之前,各级加载器都只是检查是否加载过,并不会尝试自己去加载。直到 Bootstrap ClassLoader(它没有父加载器),如果它无法加载,则会下沉到子加载器去尝试加载(调用 findClass),一直到最底层。
  5. 抛出异常:如果没有任何加载器能加载该类,最终将抛出 ClassNotFoundException

机制设计的意义

双亲委派机制的设计主要有以下好处:

防止核心类库被篡改。例如,如果有人试图替换系统级别的类(如 String.java)并篡改其实现,在这种机制下,由于系统类已经被 Bootstrap ClassLoader 加载过了(当一个类需要加载时,最先尝试加载的就是 Bootstrap ClassLoader),其他类加载器并没有机会再去加载这些类。这在一定程度上防止了危险代码的植入,保证了 Java 核心 API 的安全性。

参考文章

说明:本文基于 Java 8 及更早版本的 ClassLoader 架构(Bootstrap/Ext/App)进行描述。Java 9 引入模块系统后,类加载器层级有所调整(如引入 Platform ClassLoader 等),但双亲委派的核心思想依然适用。