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

雙親委派模型是 Java 類加載器的一種工作模式,通過這種工作模式,Java 虛擬機將類文件加載到內存中,這樣就保證了 Java 程序能夠正常的運行起來。那麼雙親委派模型究竟說的是啥呢?接下來我們一起來看。

1.類加載器

雙親委派模型針對的是 Java 虛擬機中三個類加載器的,這三個類加載器分別是:

  1. 啓動類加載器(Bootstrap ClassLoader)
  2. 擴展類加載器(Extension ClassLoader)
  3. 應用程序類加載器(Application ClassLoader)

如下圖所示:
image.png
這 3 個類加載器的作用如下。

1.1 啓動類加載器

啓動類加載器(Bootstrap ClassLoader)是由 C++ 實現的,它是用來加載 <JAVA_HOME>\jre\lib\rt.jar 和 resources.jar 等 jar 包的,如下圖所示:
image.png
接下來我們寫個代碼測試一下 rt 類加載器的打印:

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

以上程序的執行結果如下圖所示:
image.png
問題來了,爲什麼打印的不是“Bootstrap ClassLoader”而是 null 呢?
這是因爲啓動類加載器(Bootstrap ClassLoader)是由 C++ 實現的,而這個 C++ 實現的類加載器在 Java 中是沒有與之對應的類的,所以拿到的結果是 null。

1.2 擴展類加載器

擴展類加載器是用來加載 <JAVA_HOME>\jre\lib\ext 目錄下 jar 包的,如下圖所示:
image.png
接下來我們使用代碼來演示一下 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());
    }
}

以上程序的執行結果如下圖所示:
image.png

1.3 應用程序類加載器

應用程序類加載器是用來加載 classpath 也就是用戶的所有類的,接下來我們寫代碼測試一下應用程序類加載器的打印,實現代碼如下:

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

以上程序的執行結果如下圖所示:
image.png

2.雙親委派模型

雙親委派模型的執行流程是這樣的:
1、當加載一個類時,會先從應用程序類加載器的緩存裏查找相應的類,如果能找到就返回對象,如果找不到就執行下面流程;

2、在擴展加載器緩存中查找相應的類,如果能找到就返回對象,如果找不到就繼續下面流程;

3、在啓動類加載器中查詢相應的類,如果找到就返回對象,如果找不到就繼續下面流程;

4、在擴展加載器中查找並加載類,如果能找到就返回對象,並將對象加入到緩存中,如果找不到就繼續下面流程;

5、在應用程序類加載器中查找並加載類,如果能找到就返回對象,並將對象加入到緩存中,如果找不到就返回 ClassNotFound 異常。

加載流程如下圖所示:
image.png
一般“雙親”指的是“父親”和“母親”,而在這裏“雙親”指的是類加載類先向上找,再向下找的流程就叫做雙親委派模型。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章