類加載器(ClassLoader)用來(lái)加載 class字節(jié)碼到 Java 虛擬機(jī)中。一般來(lái)說(shuō),Java 虛擬機(jī)使用 Java 類的方式如下:Java 源文件在經(jīng)過(guò) Javac之后就被轉(zhuǎn)換成 Java 字節(jié)碼文件(.class 文件)。類加載器負(fù)責(zé)讀取 Java 字節(jié)代碼,并轉(zhuǎn)換成 java.lang.Class 類的一個(gè)實(shí)例。每一個(gè)這樣的實(shí)例用來(lái)表示一個(gè) Java 類。實(shí)際的情況可能更加復(fù)雜,比如 Java 字節(jié)代碼可能是通過(guò)工具動(dòng)態(tài)生成的,也可能是通過(guò)網(wǎng)絡(luò)下載。 類與類加載器 類加載器雖然只用于實(shí)現(xiàn)類的加載動(dòng)作,但它在Java程序中起到的作用卻遠(yuǎn)遠(yuǎn)不限于類加載階段。對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立其在Java虛擬中的唯一性。說(shuō)通俗一些,比較兩個(gè)類是否“相等”,只有在兩個(gè)類是由同一個(gè)類加載器的前提之下才有意義,否則,即使這兩個(gè)類來(lái)源于同一個(gè)class文件,只要加載它的類加載器不同,那這兩個(gè)類必定不相等。這里所指的“相等”包括代表類的Class對(duì)象的equal方法、isAssignableFrom()、isInstance()方法及instance關(guān)鍵字返回的結(jié)果。 類加載器分類: 主要分為Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader和User Defined ClassLoader。 啟動(dòng)類加載器(Bootstrap ClassLoader): 這個(gè)類加載器使用C++語(yǔ)言實(shí)現(xiàn),并非ClassLoader的子類。主要負(fù)責(zé)加載存放在JAVA_HOME / jre / lib / rt.jar里面所有的class文件,或者被-Xbootclasspath參數(shù)所指定路徑中以rt.jar命名的文件。 擴(kuò)展類加載器(Extension ClassLoader): 這個(gè)加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載AVA_HOME / lib / ext目錄中的,或者被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫(kù)。 應(yīng)用程序類加載器(Application ClassLoader): 這個(gè)加載器由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載classpath對(duì)應(yīng)的jar及目錄。一般情況下這個(gè)就是程序中默認(rèn)的類加載器。 自定義類加載器(User Defined ClassLoader): 開(kāi)發(fā)人員繼承ClassLoader抽象類自行實(shí)現(xiàn)的類加載器,基于自行開(kāi)發(fā)的ClassLoader可用于并非加載classpath中(例如從網(wǎng)絡(luò)上下載的jar或二進(jìn)制字節(jié)碼)、還可以在加載class文件之前做些小動(dòng)作 如:加密等。 雙親委托模型: 上圖中所展示的類加載器之間的這種層次關(guān)系,就稱為類加載器的雙親委托模型。雙親委托模型要求除了頂層的啟動(dòng)類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。這里類加載器之間的父子關(guān)系一般不會(huì)以繼承的關(guān)系來(lái)實(shí)現(xiàn),而是使用組合關(guān)系來(lái)復(fù)用父加載器的代碼。
雙親委托的工作過(guò)程:如果一個(gè)類加載器收到了一個(gè)類加載請(qǐng)求,它首先不會(huì)自己去加載這個(gè)類,而是把這個(gè)請(qǐng)求委托給父類加載器去完成,每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到頂層的啟動(dòng)類加載器中,只有當(dāng)父類加載器反饋?zhàn)约簾o(wú)法完成加載請(qǐng)求(它管理的范圍之中沒(méi)有這個(gè)類)時(shí),子加載器才會(huì)嘗試著自己去加載。 使用雙親委托模型來(lái)組織類加載器之間的關(guān)系,有一個(gè)顯而易見(jiàn)的好處就是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系,例如java.lang.Object存放在rt.jar之中,無(wú)論那個(gè)類加載器要加載這個(gè)類,最終都是委托給啟動(dòng)類加載器進(jìn)行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個(gè)類,相反,如果沒(méi)有雙親委托模型,由各個(gè)類加載器去完成的話,如果用戶自己寫(xiě)一個(gè)名為java.lang.Object的類,并放在classpath中,應(yīng)用程序中可能會(huì)出現(xiàn)多個(gè)不同的Object類,java類型體系中最基本安全行為也就無(wú)法保證。 類加載器SPI: java.lang.ClassLoader 類提供的幾個(gè)關(guān)鍵方法: loadClass: 此方法負(fù)責(zé)加載指定名字的類,首先會(huì)從已加載的類中去尋找,如果沒(méi)有找到;從parent ClassLoader[ExtClassLoader]中加載;如果沒(méi)有加載到,則從Bootstrap ClassLoader中嘗試加載(findBootstrapClassOrNull方法), 如果還是加載失敗,則拋出異常ClassNotFoundException, 在調(diào)用自己的findClass方法進(jìn)行加載。如果要改變類的加載順序可以覆蓋此方法;如果加載順序相同,則可以通過(guò)覆蓋findClass方法來(lái)做特殊處理,例如:解密,固定路徑尋找等。當(dāng)通過(guò)整個(gè)尋找類的過(guò)程仍然未獲取Class對(duì)象,則拋出ClassNotFoundException異常。 如果類需要resolve,在調(diào)用resolveClass進(jìn)行鏈接。
findLoadedClass 此方法負(fù)責(zé)從當(dāng)前ClassLoader實(shí)例對(duì)象的緩存中尋找已加載的類,調(diào)用的為native方法。
findClass 此方法直接拋出ClassNotFoundException異常,因此要通過(guò)覆蓋loadClass或此方法來(lái)以自定義的方式加載相應(yīng)的類。
findSystemClass 此方法是從sun.misc.Launcher$AppClassLoader中尋找類,如果未找到,則繼續(xù)從BootstrapClassLoader中尋找,如果仍然未找到,返回null
defineClass 此方法負(fù)責(zé)將二進(jìn)制字節(jié)流轉(zhuǎn)換為Class對(duì)象,這個(gè)方法對(duì)于自定義類加載器而言非常重要。如果二進(jìn)制的字節(jié)碼的格式不符合jvm class文件格式規(guī)范,則拋出ClassFormatError異常;如果生成的類名和二進(jìn)制字節(jié)碼不同,則拋出NoClassDefFoundError;如果加載的class是受保護(hù)的、采用不同簽名的,或者類名是以java.開(kāi)頭的,則拋出SecurityException異常。
resolveClass 此方法負(fù)責(zé)完成Class對(duì)象的鏈接,如果鏈接過(guò),則直接返回。 常見(jiàn)異常: ClassNotFoundException 這是最常見(jiàn)的異常,產(chǎn)生這個(gè)異常的原因?yàn)樵诋?dāng)前的ClassLoader 中加載類時(shí),未找到類文件, NoClassDefFoundError 這個(gè)異常是因?yàn)? 加載到的類中引用到的另外類不存在,例如要加載A,而A中盜用了B,B不存在或當(dāng)前的ClassLoader無(wú)法加載B,就會(huì)拋出這個(gè)異常。 LinkageError 該異常在自定義ClassLoader的情況下更容易出現(xiàn),主要原因是此類已經(jīng)在ClassLoader加載過(guò)了,重復(fù)的加載會(huì)造成該異常。 本系列:
關(guān)注「ImportNew」 看更多 Java 技術(shù)精選文章 ↓↓↓
|
|