雙親委派模型是 Java 類載入器的一種工作模式,通過這種工作模式,Java 虛擬機器器將類檔案載入到記憶體中,這樣就保證了 Java 程式能夠正常的執行起來。那麼雙親委派模型究竟說的是啥呢?接下來我們一起來看。
雙親委派模型針對的是 Java 虛擬機器器中三個類載入器的,這三個類載入器分別是:
如下圖所示:
這 3 個類載入器的作用如下。
啟動類載入器(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。
擴充套件類載入器是用來載入 <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());
}
}
以上程式的執行結果如下圖所示:
應用程式類載入器是用來載入 classpath 也就是使用者的所有類的,接下來我們寫程式碼測試一下應用程式類載入器的列印,實現程式碼如下:
public class ClassLoaderExample {
public static void main(String[] args) {
System.out.println("application classloader:" +
ClassLoaderExample.class.getClassLoader());
}
}
以上程式的執行結果如下圖所示:
雙親委派模型的執行流程是這樣的:
1、當載入一個類時,會先從應用程式類載入器的快取裡查詢相應的類,如果能找到就返回物件,如果找不到就執行下面流程;
2、在擴充套件載入器快取中查詢相應的類,如果能找到就返回物件,如果找不到就繼續下面流程;
3、在啟動類載入器中查詢相應的類,如果找到就返回物件,如果找不到就繼續下面流程;
4、在擴充套件載入器中查詢並載入類,如果能找到就返回物件,並將物件加入到快取中,如果找不到就繼續下面流程;
5、在應用程式類載入器中查詢並載入類,如果能找到就返回物件,並將物件加入到快取中,如果找不到就返回 ClassNotFound 異常。
載入流程如下圖所示:
一般「雙親」指的是「父親」和「母親」,而在這裡「雙親」指的是類載入類先向上找,再向下找的流程就叫做雙親委派模型。
雙親委派模型的優點有兩個:
1、安全。
2、避免重複載入。
在安全方面的表現時,當使用雙親委派模型時,使用者就不能偽造一些不安全的系統類了,比如 jre 裡面已經提供了 String 類在啟動類載入時載入,那麼使用者自定義再自定義一個不安全的 String 類時,按照雙親委派模型就不會再載入使用者定義的那個不安全的 String 類了,這樣就可以避免非安全問題的發生了。
使用雙親委派模型也可以避免一個類被重複載入,當一個類被載入之後,因為使用的雙親委派模型,這樣不會出現多個類載入器都將同一個類重複載入的情況了。
雙親委派模型的典型問題是載入 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