Java類加載器:坑爹是我的特色
本文轉(zhuǎn)載自微信公眾號(hào)「程序猿阿星」,作者程序猿阿星。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序猿阿星公眾號(hào)。
大家好,我是頭發(fā)還很多的阿星
今天的主角是「在日常開(kāi)發(fā)中,存在感非常低的類加載器」,眾所周知大佬除了頭發(fā)比較少,還非常低調(diào),如果想和大佬交朋友,那一定要先認(rèn)識(shí)大佬,下面阿星來(lái)帶大家認(rèn)識(shí)一下類加載器(本文不涉及源碼)。
什么是類加載器
我們平時(shí)寫(xiě)了那么多的Java代碼,卻不知Java類的加載過(guò)程,豈不是很尷尬,為了打破尷尬,阿星得從Java類說(shuō)起。
我們編寫(xiě)的Java類也就是.java文件,通過(guò)Java編譯器編譯成.class文件,.class文件中保存著Java代碼轉(zhuǎn)換后的虛擬機(jī)指令。
當(dāng)程序使用某個(gè)Java類時(shí),JVM虛擬機(jī)會(huì)加載它的.class文件到虛擬機(jī)的內(nèi)存中,負(fù)責(zé)加載工作的就是類加載器。
類加載過(guò)程
其實(shí)類加載器和日常生活中坐地鐵過(guò)安檢是一樣的道理,不信你看下面的圖
安檢要經(jīng)過(guò)一系列的檢查過(guò)程,目的是讓進(jìn)入地鐵站的人群符合乘坐標(biāo)準(zhǔn),比如你不能感冒,不能帶威脅人生安全的物品等。
同樣類加載也要經(jīng)過(guò)一系列檢查過(guò)程,這個(gè)過(guò)程稱為類加載過(guò)程。
類加載過(guò)程分為加載、驗(yàn)證、準(zhǔn)備、解析、初始化,下圖是對(duì)類加載過(guò)程簡(jiǎn)單的介紹。
類加載過(guò)程不是本文的重點(diǎn),如果對(duì)這塊有興趣深入研究的伙伴可以去自行百度或google。
下面要說(shuō)說(shuō)本文的重點(diǎn),雙親委派模式
實(shí)力坑爹
雙親委派機(jī)制有點(diǎn)像實(shí)力坑爹,出了什么事情都讓爹去擦屁股,爹解決不了,自己才承擔(dān)。
JVM虛擬機(jī)提供了3種類加載器,它們分別是啟動(dòng)類加載器(Bootstrap)、擴(kuò)展類加載器(Extension)、系統(tǒng)類加載器(System)。
每個(gè)類加載器都有明確的加載范圍:
- 啟動(dòng)類加載器(Bootstrap):加載
/lib路徑下的核心類庫(kù) - 擴(kuò)展類加載器(Extension):加載
/lib/extl路徑下的 - 系統(tǒng)類加載器(System):加載系統(tǒng)類路徑classpath,也就是我們經(jīng)常用到的classpath路徑,一般情況該類加載器是程序中默認(rèn)的類加載器
雙親委派模式的原理也十分簡(jiǎn)單,類加載器收到類加載請(qǐng)求,會(huì)委托給父類加載器去執(zhí)行,父類加載器還存在其父類加載器,則進(jìn)一步向上委托,依次遞歸,直到頂層類加載器,如果頂層類加載器加載到該類,就成功返回class對(duì)象,否則委托給下級(jí)類加載器去執(zhí)行,依次遞歸(此處的父子關(guān)系并非通常所說(shuō)的繼承關(guān)系,而是采用組合關(guān)系來(lái)實(shí)現(xiàn))。
用大白話來(lái)說(shuō)就是,每個(gè)兒子都很懶,有事就丟給爹去干,直到爹說(shuō)這件事我也干不了,兒子自己再想辦法完成。
雙親委派模式是為了避免重復(fù)加載和核心類篡改。
特殊需求
在日常開(kāi)發(fā)中,我們可能會(huì)有特殊的業(yè)務(wù)需求,可能就需要使用到自定義類加載器,該加載器的上級(jí)一定是系統(tǒng)類加載器。
你們想要的特殊需求
- 資源隔離
web容器可能需要部署兩個(gè)應(yīng)用程序,不同的應(yīng)用程序可能會(huì)依賴同一個(gè)第三方類庫(kù)的不同版本,因此要保證每個(gè)應(yīng)用程序的類庫(kù)都是獨(dú)立的,相互隔離
web容器有自己依賴的類庫(kù),不能與應(yīng)用程序的類庫(kù)混淆,基于安全考慮,應(yīng)該讓容器的類庫(kù)和程序的類庫(kù)隔離
- 加密保護(hù)
- 公司的一些核心類庫(kù),可能會(huì)把字節(jié)碼加密,這樣加載類的時(shí)候就必須對(duì)字節(jié)碼進(jìn)行解密
- 其他來(lái)源加載類
- 字節(jié)碼是放在數(shù)據(jù)庫(kù)、硬盤(pán)其他路徑、甚至是在云端
- 類重新加載
- JVM中類對(duì)象的唯一性:類加載器實(shí)例+完整類名
- 程序運(yùn)行中,類內(nèi)容發(fā)生變化,創(chuàng)建自定義加載器實(shí)例重新加載類,達(dá)到的熱部署效果。
這里大家有個(gè)概念,理解下就夠了,想深入探索就需要涉及源碼分析,如果大伙有興趣,評(píng)論區(qū)留言,阿星后續(xù)單獨(dú)補(bǔ)一篇源碼分析~
強(qiáng)大的父親
有些爹的實(shí)力恐怖如斯,為了啥事都能幫后代處理好,直接破壞雙親委派模式,深受孩兒們的喜愛(ài)。
Java應(yīng)用中存在著很多服務(wù)提供者接口(Service Provider Interface,SPI),這些接口允許第三方為它們提供實(shí)現(xiàn),如常見(jiàn)的SPI有JDBC、JNDI等,這些SPI的接口屬于Java核心庫(kù),一般存在rt.jar包中,由啟動(dòng)類加載器(Bootstrap)加載,而SPI的第三方實(shí)現(xiàn)代碼則是作為Java應(yīng)用所依賴的jar包被存放在classpath路徑下。
由于SPI接口中的代碼需要加載第三方實(shí)現(xiàn)類并調(diào)用其相關(guān)函數(shù),但SPI的核心接口類是由啟動(dòng)類加載器(Bootstrap)加載的,Bootstrap加載器無(wú)法直接加載SPI的實(shí)現(xiàn)類。
在這種情況下,我們就需要一種特殊的類加載器來(lái)加載第三方的類庫(kù),它就是線程上下文類加載器,線程的上下文類加載器默認(rèn)設(shè)置的就是系統(tǒng)類加載器(System)。
后面的計(jì)劃安排
給各位讀者們匯報(bào)下后續(xù)的安排,嗯....,不出意外的話,基本都是周更了,然后期間轉(zhuǎn)載一些精品的文章,大伙們看完了,記得一鍵三連啊,報(bào)告完畢!
關(guān)于我
這里是阿星,一個(gè)熱愛(ài)技術(shù)的Java程序猿,公眾號(hào) 「程序猿阿星」 里將會(huì)定期分享操作系統(tǒng)、計(jì)算機(jī)網(wǎng)絡(luò)、Java、分布式、數(shù)據(jù)庫(kù)等精品原創(chuàng)文章,2021,與您在 Be Better 的路上共同成長(zhǎng)!