JDK為何自己先破壞雙親委派模型?
? 點(diǎn)擊上方“JavaEdge”,關(guān)注公眾號(hào)
說是雙親,其實(shí)多級(jí)單親,無奈迎合歷史的錯(cuò)誤翻譯吧。

1 工作流程

當(dāng)一個(gè)類加載器收到一個(gè)類加載請(qǐng)求 在 JDK9 后,會(huì)首先搜索它的內(nèi)建加載器定義的所有“具名模塊”:
如果找到合適的模塊定義,將會(huì)使用該加載器來加載
如果未找到,則會(huì)將該請(qǐng)求委派給父級(jí)加載器去加載
因此所有的類加載請(qǐng)求最終都應(yīng)該被傳入到啟動(dòng)類加載器(Bootstrap ClassLoader)中,只有當(dāng)父級(jí)加載器反饋無法完成這個(gè)列的加載請(qǐng)求時(shí)(它的搜索范圍內(nèi)不存在這個(gè)類),子級(jí)加載器才嘗試加載。
在類路徑下找到的類將成為這些加載器的無名模塊。
這里的父子關(guān)系是組合而不是繼承。
雙親委派模型示意圖?


雙親委派模型的優(yōu)點(diǎn)

避免重復(fù)加載 父類已經(jīng)加載了,子類就不需要再次加載。eg,object 類。它存放在 rt.jar 中,無論哪個(gè)類加載器要加載這個(gè)類,最終都是委派給處于模型頂端的啟動(dòng)類加載器加載,因此 object 類在程序的各種加載環(huán)境中都是同一個(gè)類。
更安全 解決了各個(gè)類加載器的基礎(chǔ)類的統(tǒng)一問題,如果不使用該種方式,那么用戶可以隨意定義類加載器來加載核心 API,會(huì)帶來安全隱患。

雙親委派模型的實(shí)現(xiàn)



類加載的方式

通過命令行啟動(dòng)應(yīng)用時(shí)由JVM初始化加載含有main()方法的主類。
通過Class.forName()方法動(dòng)態(tài)加載,會(huì)默認(rèn)執(zhí)行初始化塊(static{}),但是Class.forName(name,initialize,loader)中的initialze可指定是否要執(zhí)行初始化塊。
通過ClassLoader.loadClass()方法動(dòng)態(tài)加載,不會(huì)執(zhí)行初始化塊。

自定義類加載器

實(shí)現(xiàn)方式

遵守雙親委派模型 繼承ClassLoader,重寫findClass()方法。
破壞雙親委派模型 繼承ClassLoader,重寫loadClass()方法。
通常我們推薦采用第一種方法自定義類加載器,最大程度上的遵守雙親委派模型。
如果有一個(gè)類加載器能加載某個(gè)類,稱為定義類加載器,所有能成功返回該類的Class的類加載器都被稱為初始類加載器。
自定義類加載的目的是想要手動(dòng)控制類的加載,那除了通過自定義的類加載器來手動(dòng)加載類這種方式,還有其他的方式么?
利用現(xiàn)成的類加載器進(jìn)行加載

利用URLClassLoader進(jìn)行加載


類加載實(shí)例

命令行下執(zhí)行HelloWorld.java

該段代碼大體經(jīng)過了一下步驟:
尋找jre目錄,尋找jvm.dll,并初始化JVM
產(chǎn)生一個(gè)Bootstrap ClassLoader
Bootstrap ClassLoader加載器會(huì)加載他指定路徑下的java核心api,并且生成Extended ClassLoader加載器的實(shí)例,然后Extended ClassLoader會(huì)加載指定路徑下的擴(kuò)展java api,并將其父設(shè)置為Bootstrap ClassLoader
Bootstrap ClassLoader生成Application ClassLoader,并將其父Loader設(shè)置為Extended ClassLoader
最后由AppClass ClassLoader加載classpath目錄下定義的類——HelloWorld類。
我們上面談到 Extended ClassLoader和Application ClassLoader是通過Launcher來創(chuàng)建,現(xiàn)在我們?cè)倏纯丛创a


破壞雙親委派模型

雙親模型的問題

父加載器無法向下識(shí)別子加載器加載的資源。
JDBC

如下證明 JDBC 是啟動(dòng)類加載器加載,但 mysql 驅(qū)動(dòng)是應(yīng)用類加載器。而 JDBC 運(yùn)行時(shí)又需要去訪問子類加載器加載的驅(qū)動(dòng),就破壞了該模型。?
?JDK 自己為解決該問題,引入線程上下問類加載器,可以通過Thread的setContextClassLoader()進(jìn)行設(shè)置
當(dāng)為啟動(dòng)類加載器時(shí),使用當(dāng)前實(shí)際加載驅(qū)動(dòng)類的類加載器?
熱替換

比如OSGI的模塊化熱部署,它的類加載器就不再是嚴(yán)格按照雙親委派模型,很多 可能就在平級(jí)的類加載器中執(zhí)行了。

FAQ

ClassLoader通過一個(gè)類全限定名來獲取二進(jìn)制流,如果我們需通過自定義類加載其來加載一個(gè)Jar包的時(shí)候,難道要自己遍歷jar中的類,然后依次通過ClassLoader進(jìn)行加載嗎?或者說我們?cè)趺磥砑虞d一個(gè)jar包呢? 對(duì)于動(dòng)態(tài)加載jar而言,JVM默認(rèn)會(huì)使用第一次加載該jar中指定類的類加載器作為默認(rèn)的ClassLoader。
假設(shè)我們現(xiàn)在存在名為sbbic的jar包,該包中存在ClassA和ClassB類(ClassA中沒有引用ClassB)。現(xiàn)在我們通過自定義的ClassLoaderA來加載在ClassA這個(gè)類,此時(shí)ClassLoaderA就成為sbbic.jar中其他類的默認(rèn)類加載器。即ClassB默認(rèn)也會(huì)通過ClassLoaderA去加載。
如果一個(gè)類引用的其他的類,那么這個(gè)其他的類由誰來加載?
如果ClassA中引用了ClassB呢? 當(dāng)類加載器在加載ClassA的時(shí)候,發(fā)現(xiàn)引用了ClassB,此時(shí)類加載如果檢測(cè)到ClassB還沒有被加載,則先回去加載。當(dāng)ClassB加載完成后,繼續(xù)回來加載ClassA。即類會(huì)通過自身對(duì)應(yīng)的來加載其加載其他引用的類。
既然類可以由不同的加載器加載,那么如何確定兩個(gè)類如何是同一個(gè)類?
JVM規(guī)定:對(duì)于任何一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立在java虛擬機(jī)中的唯一性。即在jvm中判斷兩個(gè)類是否是同一個(gè)類取決于類加載和類本身,也就是同一個(gè)類加載器加載的同一份Class文件生成的Class對(duì)象才是相同的,類加載器不同,那么這兩個(gè)類一定不相同。
往期推薦

目前交流群已有?800+人,旨在促進(jìn)技術(shù)交流,可關(guān)注公眾號(hào)添加筆者微信邀請(qǐng)進(jìn)群
喜歡文章,點(diǎn)個(gè)“在看、點(diǎn)贊、分享”素質(zhì)三連支持一下~
