面試必問(wèn):什么是雙親委派模型?
雙親委派模型是 Java 類(lèi)加載器的一種工作模式,通過(guò)這種工作模式,Java 虛擬機(jī)將類(lèi)文件加載到內(nèi)存中,這樣就保證了 Java 程序能夠正常的運(yùn)行起來(lái)。那么雙親委派模型究竟說(shuō)的是啥呢?接下來(lái)我們一起來(lái)看。
1.類(lèi)加載器
雙親委派模型針對(duì)的是 Java 虛擬機(jī)中三個(gè)類(lèi)加載器的,這三個(gè)類(lèi)加載器分別是:
- 啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader)
- 擴(kuò)展類(lèi)加載器(Extension ClassLoader)
- 應(yīng)用程序類(lèi)加載器(Application ClassLoader)
如下圖所示:
這 3 個(gè)類(lèi)加載器的作用如下。
1.1 啟動(dòng)類(lèi)加載器
啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader)是由 C++ 實(shí)現(xiàn)的,它是用來(lái)加載 <JAVA_HOME>\jre\lib\rt.jar 和 resources.jar 等 jar 包的,如下圖所示:
接下來(lái)我們寫(xiě)個(gè)代碼測(cè)試一下 rt 類(lèi)加載器的打?。?/p>
以上程序的執(zhí)行結(jié)果如下圖所示:
問(wèn)題來(lái)了,為什么打印的不是“Bootstrap ClassLoader”而是 null 呢?這是因?yàn)閱?dòng)類(lèi)加載器(Bootstrap ClassLoader)是由 C++ 實(shí)現(xiàn)的,而這個(gè) C++ 實(shí)現(xiàn)的類(lèi)加載器在 Java 中是沒(méi)有與之對(duì)應(yīng)的類(lèi)的,所以拿到的結(jié)果是 null。
1.2 擴(kuò)展類(lèi)加載器
擴(kuò)展類(lèi)加載器是用來(lái)加載 <JAVA_HOME>\jre\lib\ext 目錄下 jar 包的,如下圖所示:
接下來(lái)我們使用代碼來(lái)演示一下 ext 類(lèi)加載器,示例代碼如下:
以上程序的執(zhí)行結(jié)果如下圖所示:
1.3 應(yīng)用程序類(lèi)加載器
應(yīng)用程序類(lèi)加載器是用來(lái)加載 classpath 也就是用戶(hù)的所有類(lèi)的,接下來(lái)我們寫(xiě)代碼測(cè)試一下應(yīng)用程序類(lèi)加載器的打印,實(shí)現(xiàn)代碼如下:
以上程序的執(zhí)行結(jié)果如下圖所示:
2.雙親委派模型
雙親委派模型的執(zhí)行流程是這樣的:
1、當(dāng)加載一個(gè)類(lèi)時(shí),會(huì)先從應(yīng)用程序類(lèi)加載器的緩存里查找相應(yīng)的類(lèi),如果能找到就返回對(duì)象,如果找不到就執(zhí)行下面流程;
2、在擴(kuò)展加載器緩存中查找相應(yīng)的類(lèi),如果能找到就返回對(duì)象,如果找不到就繼續(xù)下面流程;
3、在啟動(dòng)類(lèi)加載器中查詢(xún)相應(yīng)的類(lèi),如果找到就返回對(duì)象,如果找不到就繼續(xù)下面流程;
4、在擴(kuò)展加載器中查找并加載類(lèi),如果能找到就返回對(duì)象,并將對(duì)象加入到緩存中,如果找不到就繼續(xù)下面流程;
5、在應(yīng)用程序類(lèi)加載器中查找并加載類(lèi),如果能找到就返回對(duì)象,并將對(duì)象加入到緩存中,如果找不到就返回 ClassNotFound 異常。加載流程如下圖所示:
一般“雙親”指的是“父親”和“母親”,而在這里“雙親”指的是類(lèi)加載類(lèi)先向上找,再向下找的流程就叫做雙親委派模型。
3.優(yōu)缺點(diǎn)分析
3.1 優(yōu)點(diǎn)
雙親委派模型的優(yōu)點(diǎn)有兩個(gè):1、安全。2、避免重復(fù)加載。
3.1.1 安全
在安全方面的表現(xiàn)時(shí),當(dāng)使用雙親委派模型時(shí),用戶(hù)就不能偽造一些不安全的系統(tǒng)類(lèi)了,比如 jre 里面已經(jīng)提供了 String 類(lèi)在啟動(dòng)類(lèi)加載時(shí)加載,那么用戶(hù)自定義再自定義一個(gè)不安全的 String 類(lèi)時(shí),按照雙親委派模型就不會(huì)再加載用戶(hù)定義的那個(gè)不安全的 String 類(lèi)了,這樣就可以避免非安全問(wèn)題的發(fā)生了。
3.1.2 避免重復(fù)加載
使用雙親委派模型也可以避免一個(gè)類(lèi)被重復(fù)加載,當(dāng)一個(gè)類(lèi)被加載之后,因?yàn)槭褂玫碾p親委派模型,這樣不會(huì)出現(xiàn)多個(gè)類(lèi)加載器都將同一個(gè)類(lèi)重復(fù)加載的情況了。
3.2 缺點(diǎn)
雙親委派模型的典型問(wèn)題是加載 SPI 實(shí)現(xiàn)類(lèi)的場(chǎng)景,比如 JNDI(Java Naming and Directory Interface,Java 命名與目錄接口)服務(wù),它的代碼由啟動(dòng)類(lèi)加載器去加載(在 JDK 1.3 時(shí)放進(jìn) rt.jar),但 JNDI 的目的就是對(duì)資源進(jìn)行集中管理和查找,它需要調(diào)用獨(dú)立廠商實(shí)現(xiàn)部部署在應(yīng)用程序的 classpath 下的 JNDI 接口提供者(SPI, Service Provider Interface)的代碼,但啟動(dòng)類(lèi)加載器不可能“認(rèn)識(shí)”之些代碼,這就雙親委派模型的問(wèn)題,JDBC 也是同樣的問(wèn)題。
總結(jié)
雙親委派模型是和 Java 中多個(gè)類(lèi)加載器(啟動(dòng)類(lèi)加載器、擴(kuò)展加載器、應(yīng)用程序類(lèi)加載器)的運(yùn)行規(guī)則,通過(guò)這個(gè)(雙親委派模型)規(guī)則可以避免類(lèi)的非安全問(wèn)題和類(lèi)被重復(fù)加載的問(wèn)題,但它也遇到了一些問(wèn)題,比如 JNDI 和 JDBC 不能通過(guò)這個(gè)規(guī)則進(jìn)行加載,它需要通過(guò)打破雙親委派的模型的方式來(lái)加載。