面試官:什麼是雙親委派模型?

2023-02-03 09:00:20

雙親委派模型是 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 面試有它就夠了:https://gitee.com/mydb/interview