2013年9月26日 星期四

動態載入 Class 並呼叫 Class 的 Method

有關 Java 的 ClassLoader 的描述,可以參考 [1]。
大略來說,JVM 內建有一個 BootStrapClassLoader,用來作為所有 Java 類別的 ClassLoader
BootStrapClassLoader 會載入 JVM 必要的類別(像是 rt.jar)。
根據 [1] 的描述,可以利用以下的程式碼查看 BootStrapClassLoader 載入了哪些 jar。

URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();  
for (int i = 0; i < urls.length; i++) {  
  System.out.println(urls[i].toExternalForm());  
}

BootStrapClassLoader 在載入時,同時會載入第二個 ClassLoader:Extension ClassLoader
Extension ClassLoader 主要職責是載入 JAVA_HOME 裡面額外放的函式庫,即預設是載入 /lib/ext 資料夾內的東西。
Extension ClassLoader 最後會載入 App ClassLoader,就是載入使用者執行的應用程式的 class 了。

要動態載入 Class 時,首先取得一個 ClassLoader,可以從現有的任意 Class 取得,也可以自行建立一個 ClassLoader。
取得後就可以動態地呼叫 Class、產生實體並且呼叫指定的函式了。

範例程式碼如下:
Class<?> clazz = Object.class.getClassLoader().loadClass("com.xxx.aaa.bbb");
Object instanceOfClass = clazz.newInstance();
Method getIntegerMethod = clazz.getDeclaredMethod("getObject", String.class);
Object returnedValue = getIntegerMethod.invoke(instanceOfClass, "test");
第一行是從 Object 這個物件取得它的 ClassLoader,然後用那個 ClassLoader 讀取類別 "com.xxx.aaa.bbb"。
第二行是 new 的動作,把讀出來的類別初始化。
第三行是設定要呼叫的函式是 getObject(),並且這個函式的輸入參數是一個 String
第四行就呼叫函式,呼叫時要給類別的 instance,即第二行初始化結果。
然後剛剛第三行設定了這個函式的參數是一個 String,因此後面還要給參數,例子裡給的就是 "test"。
最後回傳的東西是一個 Object,可以依照實際的回傳值做強制轉型。

另外對於靜態的函式,呼叫的方式也差不多,差別只是不需要做 new 的動作,而呼叫函式時,給的類別的 instance 是給那個類別本身。
Class<?> clazz = Object.class.getClassLoader().loadClass("com.xxx.aaa.bbb");
Method getIntegerMethod = clazz.getDeclaredMethod("getObject", String.class);
Object returnedValue = getIntegerMethod.invoke(clazz, "test");

參考資料:
1、深入分析Java ClassLoader原理
2、Difference betweeen Loading a class using ClassLoader and Class.forName
3、Dynamic Class Loading and Reloading in Java

沒有留言: