jvm classLoader architecture :
a, Bootstrap ClassLoader/啟動(dòng)類(lèi)加載器
主要負(fù)責(zé)jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項(xiàng)指定的jar包裝入工作.
b, Extension ClassLoader/擴(kuò)展類(lèi)加載器
主要負(fù)責(zé)jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作
c, System ClassLoader/系統(tǒng)類(lèi)加載器
主要負(fù)責(zé)java -classpath/-Djava.class.path所指的目錄下的類(lèi)與jar包裝入工作.
b, User Custom ClassLoader/用戶自定義類(lèi)加載器(java.lang.ClassLoader的子類(lèi))
在程序運(yùn)行期間, 通過(guò)java.lang.ClassLoader的子類(lèi)動(dòng)態(tài)加載class文件, 體現(xiàn)java動(dòng)態(tài)實(shí)時(shí)類(lèi)裝入特性.
類(lèi)加載器的特性:
1, 每個(gè)ClassLoader都維護(hù)了一份自己的名稱空間, 同一個(gè)名稱空間里不能出現(xiàn)兩個(gè)同名的類(lèi)。
2, 為了實(shí)現(xiàn)java安全沙箱模型頂層的類(lèi)加載器安全機(jī)制, java默認(rèn)采用了 ” 雙親委派的加載鏈 ” 結(jié)構(gòu).
如下圖:
Class Diagram:
類(lèi)圖中, BootstrapClassLoader是一個(gè)單獨(dú)的java類(lèi), 其實(shí)在這里, 不應(yīng)該叫他是一個(gè)java類(lèi)。
因?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)類(lèi)加載實(shí)現(xiàn) (其實(shí)我們不用關(guān)心這塊, 但是有興趣的, 可以研究一下 ):
bootstrap classLoader 類(lèi)加載原理探索
自定義類(lèi)加載器加載一個(gè)類(lèi)的步驟 :
ClassLoader 類(lèi)加載邏輯分析, 以下邏輯是除 BootstrapClassLoader 外的類(lèi)加載器加載流程:
// 檢查類(lèi)是否已被裝載過(guò) Class c = findLoadedClass(name); if (c == null ) { // 指定類(lèi)未被裝載過(guò) try { if (parent != null ) { // 如果父類(lèi)加載器不為空, 則委派給父類(lèi)加載 c = parent.loadClass(name, false ); } else { // 如果父類(lèi)加載器為空, 則委派給啟動(dòng)類(lèi)加載加載 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 啟動(dòng)類(lèi)加載器或父類(lèi)加載器拋出異常后, 當(dāng)前類(lèi)加載器將其 // 捕獲, 并通過(guò)findClass方法, 由自身加載 c = findClass(name); } }
用Class.forName加載類(lèi)
Class.forName使用的是被調(diào)用者的類(lèi)加載器來(lái)加載類(lèi)的.
這種特性, 證明了java類(lèi)加載器中的名稱空間是唯一的, 不會(huì)相互干擾.
即在一般情況下, 保證同一個(gè)類(lèi)中所關(guān)聯(lián)的其他類(lèi)都是由當(dāng)前類(lèi)的類(lèi)加載器所加載的.
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方法的類(lèi)的類(lèi)加載器
線程上下文類(lèi)加載器
java默認(rèn)的線程上下文類(lèi)加載器是 系統(tǒng)類(lèi)加載器(AppClassLoader).
// Now create the class loader to use to launch the application try { 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()。
使用線程上下文類(lèi)加載器, 可以在執(zhí)行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文里的類(lèi)加載器加載類(lèi).
典型的例子有, 通過(guò)線程上下文來(lái)加載第三方庫(kù)jndi實(shí)現(xiàn), 而不依賴于雙親委派.
大部分java app服務(wù)器(jboss, tomcat..)也是采用contextClassLoader來(lái)處理web服務(wù)。
還有一些采用 hotswap 特性的框架, 也使用了線程上下文類(lèi)加載器, 比如 seasar (full stack framework in japenese).
線程上下文從根本解決了一般應(yīng)用不能違背雙親委派模式的問(wèn)題.
使java類(lèi)加載體系顯得更靈活.
隨著多核時(shí)代的來(lái)臨, 相信多線程開(kāi)發(fā)將會(huì)越來(lái)越多地進(jìn)入程序員的實(shí)際編碼過(guò)程中. 因此,
在編寫(xiě)基礎(chǔ)設(shè)施時(shí), 通過(guò)使用線程上下文來(lái)加載類(lèi), 應(yīng)該是一個(gè)很好的選擇.
當(dāng)然, 好東西都有利弊. 使用線程上下文加載類(lèi), 也要注意, 保證多根需要通信的線程間的類(lèi)加載器應(yīng)該是同一個(gè),
防止因?yàn)椴煌念?lèi)加載器, 導(dǎo)致類(lèi)型轉(zhuǎn)換異常(ClassCastException).
自定義的類(lèi)加載器實(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.
而使用類(lèi)加載接口, 并加上自己的實(shí)現(xiàn)邏輯, 還可以定制出更多的高級(jí)特性.
比如,
一個(gè)簡(jiǎn)單的hot swap 類(lèi)加載器實(shí)現(xiàn):
import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; /** * 可以重新載入同名類(lèi)的類(lèi)加載器實(shí)現(xiàn) * * 放棄了雙親委派的加載鏈模式. * 需要外部維護(hù)重載后的類(lèi)的成員變量狀態(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è)類(lèi)的作用是可以重新載入同名的類(lèi), 但是, 為了實(shí)現(xiàn)hotswap, 老的對(duì)象狀態(tài)
需要通過(guò)其他方式拷貝到重載過(guò)的類(lèi)生成的全新實(shí)例中來(lái)。(A類(lèi)中的b實(shí)例)
而新實(shí)例所依賴的B類(lèi)如果與老對(duì)象不是同一個(gè)類(lèi)加載器加載的, 將會(huì)拋出類(lèi)型轉(zhuǎn)換異常(ClassCastException).
為了解決這種問(wèn)題, HotSwapClassLoader自定義了load方法. 即當(dāng)前類(lèi)是由自身classLoader加載的, 而內(nèi)部依賴的類(lè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:///structure-of-java-class-loader