利用URLClassLoader加載class到當前線程

 Java是利用ClassLoader將類載入內存的。在同一應用中,允許有很多個ClassLoader,通過委派機制,把裝載的任務傳遞給上級的裝載器的,依次類推,直到啓動類裝載器(沒有上級類裝載器)。如果啓動類裝載器能夠裝載這個類,那麼它會首先裝載。如果不能,則往下傳遞。當父類爲null時,JVM內置的類(稱爲:bootstrap class loader)就會充當父類。想想眼下的越來越多用XML文件做配置文件或者是描述符、部署符。其實這些通過XML文檔描述的配置信息最終都要變成Java類,基實都是通過ClassLoader來完成的。URLClassLoader是ClassLoader的子類,它用於從指向 JAR 文件和目錄的 URL 的搜索路徑加載類和資源。也就是說,通過URLClassLoader就可以加載指定jar中的class到內存中。下面來看一個例子,在該例子中,我們要完成的工作是利用URLClassLoader加載jar並運行其中的類的某個方法。首先我們定義一個接口,使所有繼承它的類都必須實現action方法。如下:

  1. public interface ActionInterface {
  2.      public String action();
  3. }

完成後將其打包爲testInterface.jar文件。接下來新建一工程,爲了編譯通過,引入之前打好的testInterface.jar包。並創建TestAction類,使它實現ActionInterface接口。如下:

  1. public class   TestAction implements ActionInterface {
  2.     public   String action() {
  3.          return " com.mxjava.TestAction.action " ;
  4.      }
  5. }

完成後將其打包爲test.jar,放在c盤根目錄下。下面要做的就是利用URLClassLoader加載並運行TestAction的action方法,並將返回的值打印在控制檯上。新建一工程,引入testInterface.jar包。並創建一可執行類(main方法),在其中加入如下代碼:

  1. URL url = new URL(“file:C: / test.jar”);
  2. URLClassLoader myClassLoader = new   URLClassLoader( new URL[] { url } );
  3. Class myClass = myClassLoader.loadClass(“com.mxjava.TestAction”);
  4. ActionInterface action = (ActionInterface)myClass.newInstance();
  5. System.out.println(action.action());

  在上面的例子中,首先利用URLClassLoader加載了C:/test.jar包,將其中的com.mxjava.TestAction類載入內存,將其強制轉型爲testInterface包中的ActionInterface類型,最後調用其action方法,並打印到控制檯中。

  執行程序後,在控制檯上如期打印出我們想要的內容。但是,事情並沒有那麼簡單,當我們將該代碼移動web應用中時,就會拋出異常。原來,Java爲我們提供了三種可選擇的ClassLoader
1. 系統類加載器或叫作應用類加載器 (system classloader or application classloader)
2. 當前類加載器
3. 當前線程類加載器

  在上例中我們使用javac命令來運行該程序,這時候使用的是系統類加載器 (system classloader)。這個類加載器處理 -classpath下的類加載工作,可以通過ClassLoader.getSystemClassLoader()方法調用。 ClassLoader 下所有的 getSystemXXX()的靜態方法都是通過這個方法定義的。在代碼中,應該儘量少地調用這個方法,以其它的類加載器作爲代理。否則代碼將只能工作在簡單的命令行應用中。當在web應用中時,服務器也是利用ClassLoader來加載class的,由於ClassLoader的不同,所以在強制轉型時JVM認定不是同一類型。(在JAVA中,一個類用其完全匹配類名(fully qualified class name)作爲標識,這裏指的完全匹配類名包括包名和類名。但在JVM中一個類用其全名和一個加載類ClassLoader的實例作爲唯一標識。因此,如果一個名爲Pg的包中,有一個名爲Cl的類,被類加載器KlassLoader的一個實例kl1加載,Cl的實例,即C1.class在JVM中表示爲(Cl, Pg, kl1)。這意味着兩個類加載器的實例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的,被它們所加載的類也因此完全不同,互不兼容的。)爲了能夠使程序正確運行,我們首要解決的問題就是,如何將URLClassLoader加載的類,同當前ClassLoader保持在同一類加載器中。解決方法很簡單,利用java提供的第三種ClassLoader—當前線程類加載器即可。jdk api文檔就會發現,URLClassLoader提供了三種構造方式:

  1. // 使用默認的委託父 ClassLoader 爲指定的 URL 構造一個新 URLClassLoader。
  2. URLClassLoader(URL[] urls)
  3. // 爲給定的 URL 構造新 URLClassLoader。
  4. URLClassLoader(URL[] urls, ClassLoader parent)
  5. // 爲指定的 URL、父類加載器和 URLStreamHandlerFactory 創建新 URLClassLoader。
  6. URLClassLoader(URL[] urls, ClassLoader parent,
  7.                 URLStreamHandlerFactory factory)

接下來要做的就是,在構造URLClassLoader時,將當前線程類加載器置入即可。如下:

  1. URLClassLoader myClassLoader = new URLClassLoader(new URL[] {url} ,
  2.          Thread.currentThread().getContextClassLoader());

總結:
  Java是利用ClassLoader來加載類到內存的,ClassLoader本身是用java語言寫的,所以我們可以擴展自己的ClassLoader。利用URLClassLoader可以加載指定jar包中的類到內存。在命行上利用URLClassLoader加載jar時,是使用系統類加載器來加載class的,所以在web環境下,就會出錯。這是因爲JVM中一個類用其全名和一個加載類ClassLoader的實例作爲唯一標識的。我們只要利用URLClassLoader的第二種構造方法並傳入當前線程類加載器即可解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章