打破類加載的委託模式,指定類加載器的小技巧

 
     目前java中的類加載是通過委託機制來完成的,也就是說一個類加載器加載一個類的時候會先委託它的父級類裝載器去加載,如果他的父親還有父親就在委託他父親的父親去加載,就這樣一直追溯到根類加載器BootStrap,如果BootStrap加載不了目標類,在由BootStrap的下一級去加載,這樣一級一級的回退,當回退到最初的類裝載器時,如果它自己也不能完成類裝載,會拋ClassNotFoundException異常,這樣雖然有很多好處,最大的好處就是不容易發生類轉換異常,而且類加載器也不會過於混亂,但是有的時候我們希望用指定的類加載器去加載一個類,而不是委託他的父親。例如如下場景:
src
   com
       syj
          test
             A.java
             B.java
          cp
             MyClassLoader.java
          Main.java 
默認情況下上面的程序如果用java Main啓動的話。所有類的類加載器都是AppClassLoader由類加載機制可知,如果在Main.java中new A()的話那面A類的加載器也是AppClassLoader了,如果在A中在引入B那B的類加載器也是AppClassLoader,那麼如果我想使A類有自己類加載環境的話怎麼辦呢,我希望指定A類的類加載路徑爲D盤,B類的加載環境爲E盤,這樣在A類中依賴的jar文件我可以放在D盤根目錄,而B類依賴的jar可以放置在E盤下面。呵呵,這個場景很像tomcat下Webapp中應用隔離的實現,但是tomcat中的實現太複雜了,還沒研究過。迴歸正題,要實現這個需求最初的設想是:只要我們new兩個URLClassLoader對象就可以了,然後分別指定兩個類加載對象中的類加載路徑。在Main.java中分別用兩個類加載器加載A類和B類。問題的關鍵就在這裏。A,B和Main類都是在AppClassLoader的類加載路徑中,也就是說程序啓動靠Main.java類中的main方法,如果程序可以正常啓動AppClassLoader就找到了Main.java類,既然找到了Main類那也就可以找到A和B類, URLClassLoader類加載對象是在Main中實例化的,也就是說URLClassLoader類加載器的父類加載器是AppClassLoader由類加載的委託機制可知,如果用URLClassLoader去加載A和B,結果一定還是靠AppClassLoader來加載,而不是我們指定了類加載路徑的URLClassLoader類加載器.這樣由於A依賴了D盤的jar文件,而AppClassLoader的類路徑又沒有指定D盤,就會報類找不到的錯誤.這就是委託機制帶來的困擾.
那麼如何改實現這樣的需求呢,暫時我能想到的就是自己控制類加載的方式,下面是實現方法,順便把測試代碼也寫裏面了,還有註釋:
package com.syj;

import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;

import com.syj.test.A;

/**
 * 

* Title: 自定義的類加載器Demo *

* *

* Copyright: Copyright (c) 2007 *

* * @author 孫鈺佳 * @qq 4115291 * @main [email protected] * @date Jun 8, 2008 2:06:15 PM */ public class MyClassLoader extends URLClassLoader { public MyClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { super(urls, parent, factory); } public MyClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } public MyClassLoader(URL[] urls) { super(urls); } /** * 當加載com.syj.test包下的類時先自己找,找不到再使用jvm默認的加載順序 */ protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.startsWith("com.syj.test")) return myLoadClass(name, resolve); else return super.loadClass(name, resolve); } /** * * Description:先判斷類是否已經加載,如果沒有加載先在自己的URL下找,找不到再使用jvm默認的委託機制查找 * (注:正常類加載順序是先找最上層如果找不到依次往下找,這裏先找自己如果找不到遵循正常的查找原則,從上向下重找一次) * * @param name * @param resolve * @return * @throws ClassNotFoundException * @author 孫鈺佳 * @since:2008-6-8 下午02:10:07 */ protected synchronized Class myLoadClass(String name, boolean resolve) throws ClassNotFoundException { Class c = findLoadedClass(name); if (c == null) { try { c = findClass(name); } catch (ClassNotFoundException e) { return super.loadClass(name, resolve); } } return c; } public static void main(String[] args) throws Exception { URL[] url = new URL[] { MyClassLoader.class.getClassLoader() .getResource("") };// MyClassLoader mc = new MyClassLoader(url); // 這地方不能強轉換爲A,因爲不是一個類加載器加載的對象是不能轉換的,例如A a=(A),這裏的A是AppClassLoader加載的 System.out.println(A.class.getClassLoader().getClass().getName());// 打印sun.misc.Launcher$AppClassLoader Object a = mc.loadClass("com.syj.test.A").newInstance(); System.out.println(a.getClass().getClassLoader().getClass().getName());// 打印com.syj.MyClassLoader // 如果想調用a的方法只能使用反射了 } }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章