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)行期間, 通過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)造起來的, 負(fù)責(zé)java平臺(tái)核心庫。(具體上面已經(jīng)有介紹)
啟動(dòng)類加載實(shí)現(xiàn) (其實(shí)我們不用關(guān)心這塊, 但是有興趣的, 可以研究一下 ):
bootstrap classLoader 類加載原理探索
自定義類加載器加載一個(gè)類的步驟 :
ClassLoader 類加載邏輯分析, 以下邏輯是除 BootstrapClassLoader 外的類加載器加載流程:
// 檢查類是否已被裝載過 Class c = findLoadedClass(name); if (c == null ) { // 指定類未被裝載過 try { if (parent != null ) { // 如果父類加載器不為空, 則委派給父類加載 c = parent.loadClass(name, false ); } else { // 如果父類加載器為空, 則委派給啟動(dòng)類加載加載 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 啟動(dòng)類加載器或父類加載器拋出異常后, 當(dāng)前類加載器將其 // 捕獲, 并通過findClass方法, 由自身加載 c = findClass(name); } }
用Class.forName加載類
Class.forName使用的是被調(diào)用者的類加載器來加載類的.
這種特性, 證明了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 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的無參構(gòu)造函數(shù)Launch()。
使用線程上下文類加載器, 可以在執(zhí)行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文里的類加載器加載類.
典型的例子有, 通過線程上下文來加載第三方庫jndi實(shí)現(xiàn), 而不依賴于雙親委派.
大部分java app服務(wù)器(jboss, tomcat..)也是采用contextClassLoader來處理web服務(wù)。
還有一些采用 hotswap 特性的框架, 也使用了線程上下文類加載器, 比如 seasar (full stack framework in japenese).
線程上下文從根本解決了一般應(yīng)用不能違背雙親委派模式的問題.
使java類加載體系顯得更靈活.
隨著多核時(shí)代的來臨, 相信多線程開發(fā)將會(huì)越來越多地進(jìn)入程序員的實(shí)際編碼過程中. 因此,
在編寫基礎(chǔ)設(shè)施時(shí), 通過使用線程上下文來加載類, 應(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提供給開發(fā)人員, 用來自定義加載class的接口.
使用該接口, 可以動(dòng)態(tài)的加載class文件.
例如,
在jdk中, URLClassLoader是配合findClass方法來使用defineClass, 可以從網(wǎng)絡(luò)或硬盤上加載class.
而使用類加載接口, 并加上自己的實(shí)現(xiàn)邏輯, 還可以定制出更多的高級(jí)特性.
比如,
一個(gè)簡單的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)
需要通過其他方式拷貝到重載過的類生成的全新實(shí)例中來。(A類中的b實(shí)例)
而新實(shí)例所依賴的B類如果與老對(duì)象不是同一個(gè)類加載器加載的, 將會(huì)拋出類型轉(zhuǎn)換異常(ClassCastException).
為了解決這種問題, 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:///structure-of-java-class-loader