双亲派遣模型是 Java 类加载器的一种作业形式,经过这种作业形式,Java 虚拟机将类文件加载到内存中,这样就确保了 Java 程序能够正常的运转起来。那么双亲派遣模型究竟说的是啥呢?接下来咱们一同来看。

1.类加载器

双亲派遣模型针对的是 Java 虚拟机中三个类加载器的,这三个类加载器分别是:

  1. 发动类加载器(Bootstrap ClassLoader)
  2. 扩展类加载器(Extension ClassLoader)
  3. 应用程序类加载器(Application ClassLoader)

如下图所示:

面试官:什么是双亲委派模型?
这 3 个类加载器的作用如下。

1.1 发动类加载器

发动类加载器(Bootstrap ClassLoader)是由 C++ 完成的,它是用来加载 <JAVA_HOME>\jre\lib\rt.jar 和 resources.jar 等 jar 包的,如下图所示:

面试官:什么是双亲委派模型?
接下来咱们写个代码测验一下 rt 类加载器的打印:

public class ClassLoaderExample {
    public static void main(String[] args) {
        // rt 类下的 ClassLoader 打印
        System.out.println("rt classloader:" + String.class.getClassLoader());
    }
}

以上程序的履行成果如下图所示:

面试官:什么是双亲委派模型?
问题来了,为什么打印的不是“Bootstrap ClassLoader”而是 null 呢? 这是因为发动类加载器(Bootstrap ClassLoader)是由 C++ 完成的,而这个 C++ 完成的类加载器在 Java 中是没有与之对应的类的,所以拿到的成果是 null。

1.2 扩展类加载器

扩展类加载器是用来加载 <JAVA_HOME>\jre\lib\ext 目录下 jar 包的,如下图所示:

面试官:什么是双亲委派模型?
接下来咱们使用代码来演示一下 ext 类加载器,示例代码如下:

public class ClassLoaderExample {
    public static void main(String[] args) {
        // ext 类下 classloader 打印
        System.out.println("ext classloader:" +
                sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
    }
}

以上程序的履行成果如下图所示:

面试官:什么是双亲委派模型?

1.3 应用程序类加载器

应用程序类加载器是用来加载 classpath 也便是用户的一切类的,接下来咱们写代码测验一下应用程序类加载器的打印,完成代码如下:

public class ClassLoaderExample {
    public static void main(String[] args) {
        System.out.println("application classloader:" +
                ClassLoaderExample.class.getClassLoader());
    }
}

以上程序的履行成果如下图所示:

面试官:什么是双亲委派模型?

2.双亲派遣模型

双亲派遣模型的履行流程是这样的: 1、当加载一个类时,会先从应用程序类加载器的缓存里查找相应的类,假如能找到就回来目标,假如找不到就履行下面流程;

2、在扩展加载器缓存中查找相应的类,假如能找到就回来目标,假如找不到就持续下面流程;

3、在发动类加载器中查询相应的类,假如找到就回来目标,假如找不到就持续下面流程;

4、在扩展加载器中查找并加载类,假如能找到就回来目标,并将目标加入到缓存中,假如找不到就持续下面流程;

5、在应用程序类加载器中查找并加载类,假如能找到就回来目标,并将目标加入到缓存中,假如找不到就回来 ClassNotFound 异常。

加载流程如下图所示:

面试官:什么是双亲委派模型?
一般“双亲”指的是“父亲”和“母亲”,而在这儿“双亲”指的是类加载类先向上找,再向下找的流程就叫做双亲派遣模型。

3.优缺点剖析

3.1 长处

双亲派遣模型的长处有两个: 1、安全。 2、防止重复加载。

3.1.1 安全

在安全方面的表现时,当使用双亲派遣模型时,用户就不能伪造一些不安全的体系类了,比方 jre 里边现已提供了 String 类在发动类加载时加载,那么用户自定义再自定义一个不安全的 String 类时,按照双亲派遣模型就不会再加载用户定义的那个不安全的 String 类了,这样就能够防止非安全问题的发生了。

3.1.2 防止重复加载

使用双亲派遣模型也能够防止一个类被重复加载,当一个类被加载之后,因为使用的双亲派遣模型,这样不会呈现多个类加载器都将同一个类重复加载的情况了。

3.2 缺点

双亲派遣模型的典型问题是加载 SPI 完成类的场景,比方 JNDI(Java Naming and Directory Interface,Java 命名与目录接口)服务,它的代码由发动类加载器去加载(在 JDK 1.3 时放进 rt.jar),但 JNDI 的目的便是对资源进行会集管理和查找,它需求调用独立厂商完成部部署在应用程序的 classpath 下的 JNDI 接口提供者(SPI, Service Provider Interface)的代码,但发动类加载器不可能“知道”之些代码,这就双亲派遣模型的问题,JDBC 也是相同的问题。

总结

双亲派遣模型是和 Java 中多个类加载器(发动类加载器、扩展加载器、应用程序类加载器)的运转规矩,经过这个(双亲派遣模型)规矩能够防止类的非安全问题和类被重复加载的问题,但它也遇到了一些问题,比方 JNDI 和 JDBC 不能经过这个规矩进行加载,它需求经过打破双亲派遣的模型的方法来加载。

本文已收录到 Gitee 开源库房《Java 面试指南》,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计形式、消息队列等模块。Java 面试有它就够了:gitee.com/mydb/interv…