前言
類加載技術(shù)是java運(yùn)行的核心部分之一,雖然對(duì)于開發(fā)來說運(yùn)用到此技術(shù)的地方不多,但是作為JAVAEE防盜版技術(shù)的組成部分之一,這一部分對(duì)于研發(fā)來說也需要著重了解。
本文分析對(duì)象針對(duì)于:JDK1.7
JVM預(yù)定義的三種類加載器
1.啟動(dòng)類加載器:啟動(dòng)類裝入器是用本地代碼實(shí)現(xiàn)的類裝入器,它負(fù)責(zé)將JRE/lib下面的核心類庫或-Xbootclasspath選項(xiàng)指定的jar包加載到內(nèi)存中。由于其涉及到虛擬機(jī)本地實(shí)現(xiàn)細(xì)節(jié),開發(fā)者無法直接獲取到啟動(dòng)類加載器的引用。
2.擴(kuò)展類加載器:擴(kuò)展類加載器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實(shí)現(xiàn)的。它負(fù)責(zé)將JRE/lib/ext或者由系統(tǒng)變量-Djava.ext.dir指定位置中的類庫加載到內(nèi)存中。開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。
3.系統(tǒng)類加載器:系統(tǒng)類加載器是由 Sun的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實(shí)現(xiàn)的。它負(fù)責(zé)將系統(tǒng)類路徑java -classpath或-Djava.class.path變量所指的目錄下的類庫加載到內(nèi)存中。開發(fā)者可以直接使用系統(tǒng)類加載器。
類加載的雙親委派機(jī)制
JVM在加載類時(shí)默認(rèn)采用的是雙親委派機(jī)制。通俗的講,就是某個(gè)特定的類加載器在接到加載類的請(qǐng)求時(shí),首先將加載任務(wù)委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務(wù),就成功返回;只有父類加載器無法完成此加載任務(wù)時(shí),才自己去加載。關(guān)于虛擬機(jī)默認(rèn)的雙親委派機(jī)制,我們可以從系統(tǒng)類加載器和擴(kuò)展類加載器為例作簡單分析。
圖1.1 ExtClassLoader和AppClassLoader繼承結(jié)構(gòu)圖
由圖1.1可以看到,擴(kuò)展類加載器和系統(tǒng)類加載器的層次結(jié)構(gòu)一致,并且查看源碼可知,系統(tǒng)類加載器的構(gòu)造器和擴(kuò)展類加載器的構(gòu)造器在創(chuàng)建時(shí)最終都會(huì)調(diào)用ClassLoader的帶參構(gòu)造器,并將父構(gòu)造器注冊(cè)到其中(圖1.2)。
圖1.2 擴(kuò)展類加載器的構(gòu)建過程,指定父類加載器為null
由于可以看出來,擴(kuò)展類加載器在構(gòu)建時(shí)候就指定了父加載器,并且ClassLoader類的parent方法權(quán)限為private,并且沒有提供setter方法。并且擴(kuò)展類加載器的父加載器被設(shè)置為了null。
同理,系統(tǒng)類加載器的父加載器被設(shè)置為了ExtClassLoader(圖1.3)。
圖1.3 系統(tǒng)類加載器的構(gòu)建過程,指定父類加載器為ExtClassLoader
用代碼來檢測一下:
- package com.noryar.classloader.test;
- public class ClassLoaderTest {
- public static void main(String[] args) {
- System.out.println("系統(tǒng)類加載器為:"+ClassLoader.getSystemClassLoader());
- System.out.println("擴(kuò)展類加載器為:"+ClassLoader.getSystemClassLoader().getParent());
- System.out.println("啟動(dòng)類加載器為:"+ClassLoader.getSystemClassLoader().getParent().getParent());
- }
- }
運(yùn)行結(jié)果如下,符合預(yù)期:
- 系統(tǒng)類加載器為:sun.misc.Launcher$AppClassLoader@1b31c23
- 擴(kuò)展類加載器為:sun.misc.Launcher$ExtClassLoader@1fc7b3a
- 啟動(dòng)類加載器為:null
OK,說了這么多,接下來介紹一下雙親委派機(jī)制的實(shí)現(xiàn)。首先我們分析一下ClassLoader這個(gè)抽象類的幾個(gè)重要方法。
- public abstract class ClassLoader {
- // 用指定的二進(jìn)制名稱加載類,它會(huì)通知JVM去解析類信息。
- public Class<?> loadClass(String name) throws ClassNotFoundException {
- return loadClass(name, false);
- }
-
- // 用指定的二進(jìn)制名稱加載類,具體的實(shí)現(xiàn)流程如下:
- // 1. 使用findLoadedClass(String)方法來檢測該類是否已被加載
- // 2. 調(diào)用父加載器的loadClass(String)方法,這里就是雙親委派機(jī)制的實(shí)現(xiàn)邏輯
- // 3. 調(diào)用findClass來尋找類
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- ....
- }
-
- // 使用指定的二進(jìn)制名稱尋找類。該應(yīng)當(dāng)在ClassLoader的子類中進(jìn)行重寫,并且會(huì)被loadClass方法調(diào)用。
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- throw new ClassNotFoundException(name);
- }
-
- // 將字節(jié)碼文件轉(zhuǎn)換成實(shí)體類,被findClass方法調(diào)用
- // 次方法調(diào)用本地方法,因此在開發(fā)中無需復(fù)寫。
- protected final Class<?> defineClass(String name, byte[] b, int off, int len,
- ProtectionDomain protectionDomain)
- throws ClassFormatError
- {
- ....
- }
- }
檢查發(fā)現(xiàn)ClassLoader的子類只對(duì)loadClass方法進(jìn)行了擴(kuò)展(主要是增加了一些校驗(yàn)),并沒有對(duì)其調(diào)用機(jī)理做根本改變,因此,雙親委派機(jī)制的實(shí)現(xiàn)只要分析ClassLoader的loadClass(String name, boolean resolve)方法即可。下面是該方法的具體實(shí)現(xiàn)(圖1.4)。
圖1.4 ClassLoader的loadClass方法實(shí)現(xiàn)
綜上所述,當(dāng)一個(gè)類需要加載的時(shí)候,當(dāng)前類加載器就會(huì)一級(jí)一級(jí)的調(diào)用系統(tǒng)類加載器->擴(kuò)展類加載器->啟動(dòng)類加載器進(jìn)行加載,并且最終由啟動(dòng)類加載器首先嘗試加載,加載失敗在給擴(kuò)展類加載器加載,失敗在給系統(tǒng)類加載,失敗給當(dāng)前類加載器加載。任意一級(jí)加載成功則直接返回,如果都失敗,則拋出ClassNotFoundException異常。
|