jvm classLoader architecture :
a, Bootstrap ClassLoader/啟動(dòng)類加載器
主要負(fù)責(zé)jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項(xiàng)指定的jar包裝入工作.
b, Extension ClassLoader/擴(kuò)展類加載器
主要負(fù)責(zé)jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作
c, System ClassLoader/系統(tǒng)類加載器
主要負(fù)責(zé)java -classpath/-Djava.class.path所指的目錄下的類與jar包裝入工作.
b, User Custom ClassLoader/用戶自定義類加載器(java.lang.ClassLoader的子類)
在程序運(yùn)行期間, 通過(guò)java.lang.ClassLoader的子類動(dòng)態(tài)加載class文件, 體現(xiàn)java動(dòng)態(tài)實(shí)時(shí)類裝入特性.
類加載器的特性:
1, 每個(gè)ClassLoader都維護(hù)了一份自己的名稱空間, 同一個(gè)名稱空間里不能出現(xiàn)兩個(gè)同名的類。
2, 為了實(shí)現(xiàn)java安全沙箱模型頂層的類加載器安全機(jī)制, java默認(rèn)采用了 ” 雙親委派的加載鏈 ” 結(jié)構(gòu).
如下圖:

Class Diagram:

類圖中, BootstrapClassLoader是一個(gè)單獨(dú)的java類, 其實(shí)在這里, 不應(yīng)該叫他是一個(gè)java類。
因?yàn)椋?它已經(jīng)完全不用java實(shí)現(xiàn)了。
它是在jvm啟動(dòng)時(shí), 就被構(gòu)造起來(lái)的, 負(fù)責(zé)java平臺(tái)核心庫(kù)。(具體上面已經(jīng)有介紹)
啟動(dòng)類加載實(shí)現(xiàn) (其實(shí)我們不用關(guān)心這塊, 但是有興趣的, 可以研究一下 ):
bootstrap classLoader 類加載原理探索
自定義類加載器加載一個(gè)類的步驟 :

ClassLoader 類加載邏輯分析, 以下邏輯是除 BootstrapClassLoader 外的類加載器加載流程:
// 檢查類是否已被裝載過(guò)Class c = findLoadedClass(name);if (c == null ) {// 指定類未被裝載過(guò)try {if (parent != null ) {// 如果父類加載器不為空, 則委派給父類加載c = parent.loadClass(name, false );} else {// 如果父類加載器為空, 則委派給啟動(dòng)類加載加載c = findBootstrapClass0(name);}} catch (ClassNotFoundException e) {// 啟動(dòng)類加載器或父類加載器拋出異常后, 當(dāng)前類加載器將其// 捕獲, 并通過(guò)findClass方法, 由自身加載c = findClass(name);}}
用Class.forName加載類
Class.forName使用的是被調(diào)用者的類加載器來(lái)加載類的.
這種特性, 證明了java類加載器中的名稱空間是唯一的, 不會(huì)相互干擾.
即在一般情況下, 保證同一個(gè)類中所關(guān)聯(lián)的其他類都是由當(dāng)前類的類加載器所加載的.
public static Class forName(String className)throws ClassNotFoundException {return forName0(className, true , ClassLoader.getCallerClassLoader());} /** Called after security checks have been made. */private static native Class forName0(String name, boolean initialize,ClassLoader loader)throws ClassNotFoundException;
上圖中 ClassLoader.getCallerClassLoader 就是得到調(diào)用當(dāng)前forName方法的類的類加載器
線程上下文類加載器
java默認(rèn)的線程上下文類加載器是 系統(tǒng)類加載器(AppClassLoader).
// Now create the class loader to use to launch the applicationtry {loader = AppClassLoader.getAppClassLoader(extcl);} catch (IOException e) {throw new InternalError("Could not create application class loader" );} // Also set the context class loader for the primordial thread.Thread.currentThread().setContextClassLoader(loader);
以上代碼摘自sun.misc.Launch的無(wú)參構(gòu)造函數(shù)Launch()。
使用線程上下文類加載器, 可以在執(zhí)行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文里的類加載器加載類.
典型的例子有, 通過(guò)線程上下文來(lái)加載第三方庫(kù)jndi實(shí)現(xiàn), 而不依賴于雙親委派.
大部分java app服務(wù)器(jboss, tomcat..)也是采用contextClassLoader來(lái)處理web服務(wù)。
還有一些采用 hotswap 特性的框架, 也使用了線程上下文類加載器, 比如 seasar (full stack framework in japenese).
線程上下文從根本解決了一般應(yīng)用不能違背雙親委派模式的問(wèn)題.
使java類加載體系顯得更靈活.
隨著多核時(shí)代的來(lái)臨, 相信多線程開(kāi)發(fā)將會(huì)越來(lái)越多地進(jìn)入程序員的實(shí)際編碼過(guò)程中. 因此,
在編寫(xiě)基礎(chǔ)設(shè)施時(shí), 通過(guò)使用線程上下文來(lái)加載類, 應(yīng)該是一個(gè)很好的選擇.
當(dāng)然, 好東西都有利弊. 使用線程上下文加載類, 也要注意, 保證多根需要通信的線程間的類加載器應(yīng)該是同一個(gè),
防止因?yàn)椴煌念惣虞d器, 導(dǎo)致類型轉(zhuǎn)換異常(ClassCastException).
自定義的類加載器實(shí)現(xiàn)
defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)
是java.lang.Classloader提供給開(kāi)發(fā)人員, 用來(lái)自定義加載class的接口.
使用該接口, 可以動(dòng)態(tài)的加載class文件.
例如,
在jdk中, URLClassLoader是配合findClass方法來(lái)使用defineClass, 可以從網(wǎng)絡(luò)或硬盤(pán)上加載class.
而使用類加載接口, 并加上自己的實(shí)現(xiàn)邏輯, 還可以定制出更多的高級(jí)特性.
比如,
一個(gè)簡(jiǎn)單的hot swap 類加載器實(shí)現(xiàn):
import java.io.File;import java.io.FileInputStream;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader; /*** 可以重新載入同名類的類加載器實(shí)現(xiàn)* * 放棄了雙親委派的加載鏈模式.* 需要外部維護(hù)重載后的類的成員變量狀態(tài).** @author ken.wu* @mail ken.wug@gmail.com* 2007-9-28 下午01:37:43*/public class HotSwapClassLoader extends URLClassLoader { public HotSwapClassLoader(URL[] urls) {super (urls);} public HotSwapClassLoader(URL[] urls, ClassLoader parent) {super (urls, parent);} public Class load(String name)throws ClassNotFoundException {return load(name, false );} public Class load(String name, boolean resolve)throws ClassNotFoundException {if ( null != super .findLoadedClass(name))return reload(name, resolve); Class clazz = super .findClass(name); if (resolve)super .resolveClass(clazz); return clazz;} public Class reload(String name, boolean resolve)throws ClassNotFoundException {return new HotSwapClassLoader( super .getURLs(), super .getParent()).load(name, resolve);}} public class A {private B b; public void setB(B b) {this .b = b;} public B getB() {return b;}} public class B {}
這個(gè)類的作用是可以重新載入同名的類, 但是, 為了實(shí)現(xiàn)hotswap, 老的對(duì)象狀態(tài)
需要通過(guò)其他方式拷貝到重載過(guò)的類生成的全新實(shí)例中來(lái)。(A類中的b實(shí)例)
而新實(shí)例所依賴的B類如果與老對(duì)象不是同一個(gè)類加載器加載的, 將會(huì)拋出類型轉(zhuǎn)換異常(ClassCastException).
為了解決這種問(wèn)題, HotSwapClassLoader自定義了load方法. 即當(dāng)前類是由自身classLoader加載的, 而內(nèi)部依賴的類還是老對(duì)象的classLoader加載的.
public class TestHotSwap {public static void main(String args[]) {A a = new A();B b = new B();a.setB(b); System.out.printf("A classLoader is %s n" , a.getClass().getClassLoader());System.out.printf("B classLoader is %s n" , b.getClass().getClassLoader());System.out.printf("A.b classLoader is %s n" , a.getB().getClass().getClassLoader()); HotSwapClassLoader c1 = new HotSwapClassLoader( new URL[]{ new URL( "file:\e:\test\")} , a.getClass().getClassLoader());Class clazz = c1.load(" test.hotswap.A ");Object aInstance = clazz.newInstance(); Method method1 = clazz.getMethod(" setB ", B.class);method1.invoke(aInstance, b); Method method2 = clazz.getMethod(" getB ", null);Object bInstance = method2.invoke(aInstance, null); System.out.printf(" reloaded A.b classLoader is %s n", bInstance.getClass().getClassLoader());}}
輸出
A classLoader is sun.misc.Launcher$AppClassLoader@19821f
B classLoader is sun.misc.Launcher$AppClassLoader@19821f
A.b classLoader is sun.misc.Launcher$AppClassLoader@19821f
reloaded A.b classLoader is sun.misc.Launcher$AppClassLoader@19821f
轉(zhuǎn)載請(qǐng)注明原文鏈接:http://kenwublog.com/structure-of-java-class-loader