双亲委派模型

104 阅读2分钟

前言

  • 从 Java 虚拟机角度看,存在 启动类加载器 (Boostrap Class Loader) 和 其他所有的类加载器 两种类加载器。前一种是 C++ 实现的,是虚拟机自身的一部分;后一种是 Java 实现的,独立于虚拟机外部。

三层类加载器

  1. 启动类加载器 (Bootstrap Class Loader)
  • 负责加载存放在 <JAVA_HOME>\lib 目录,或者被 -Xbootclasspath 参数所指定的路径存放,是 Java 虚拟机能够识别的类库加载到虚拟机内存中。
  1. 拓展类加载器 (Extention Class Loader)
  • 负责加载 <JAVA_HOME>\lib\ext 目录,或者被 java.ext.dirs 系统变量所指定的路径中所有的类库。它是在 sun.misc.Launcher$ExtClassLoader 中以 Java 代码的形式实现的。
  1. 应用程序类加载器 (Application Class Loader)
  • 负责加载用户类路径 (ClassPath) 上的所有类库,开发者可以直接在代码中使用这个类。它是由 sun.misc.Lauuncher$AppClassLoader 来实现。在一些场合也被称为“系统类加载器”。

双亲委派模型 & 工作过程

  • 双亲委派图示。 20220211-210635.jpg
  • 双亲委派工作过程:如果一个类加载器收到类加载的请求,它会首先把请求派给父类加载器去完成,每一个层次都是如此,因此所有的加载请求最终都送到了启动类加载器中,只有当父加载器反馈无法完成时,子加载器才会尝试自己加载。
  • 使用双亲委派的好处是 Java 的类随着它的类加载器一起具备了一种优先级层次的关系。例如 java.lang.Object,存放在 rt.jar 中,无论哪一个类要加载这个类,最终都会委派给启动类加载器加载。如果没有采用双亲委派模型,由各自类加载器去加载,那么最后在系统中就会多个不同的 Object 类,从而造成混乱。示例代码见下。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    // 检查请求是否已经被加载过
    Class c = findLoadClass()name;
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name,false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 如果父类加载器抛出异常,则说明父类加载器无法完成加载请求
        }
        if (c == null) {
            // 在父类中无法加载,再调用本身的 findClass 方法加载
            c = findClass(name);
        }
    }
    if (resolve == true) {
        resolveClass(c);
    }
    return c;
}
  • 由上面代码可看出,首先先检查请求的类型是否已经被加载过,若没有则调用父类加载器的 loadClass() 方法,若父类加载器为空则默认使用启动类加载器作为父加载器。如果父加载器加载失败的话,会抛出 ClassNotFoundException 异常,才会调用自己的findClass() 方法尝试加载。