這次要談的問題是我最近的一些心得,試了蠻久的東西, 跟大家分享一下。通常在開發應用程式的時候,做到後來總是 希望自己的應用程式能夠有擴充的能力,在java的做法常常是 另外設計一個放置擴充程式的資料夾,允許使用者或其他開發者 將自行設計的函式庫作成.jar的格式,放到這個資料夾中讓應用程式 自動載入。像這樣的技巧,通常叫做dynamic loading。 要實現dynamic loading有幾個做法,因為java在讀取class資訊的時候, 可以從.jar檔案或從一個完整的package路徑讀取,因此最簡單的方法 就是讓應用程式在啟動的時候,自動將擴充資料夾中所有的.jar檔案 加到classpath這個環境變數之中,再利用reflection機制來產生物件。 不過這個方法如果沒有經過一些設計是很容易失敗的,原因和解決方法 容後說明。第二種方法的原理很簡單,就是把.jar檔案中的整個package 解壓縮出來,然後放置在目前的工作路徑底下,如此一來只要目前的工作 路徑有在classpath中就可以輕鬆的產生物件,但是這種方法稍欠效率, 因此一般使用的機會不大。第三種方法是利用java.util.zip.ZipEntry 將.jar檔案中的class檔案使用byte []的方式讀出來,這時再改寫ClassLoader ,使用ClassLoader中的defineClass這個韓是從byte []建立Class,再從 Class的newInstance函式建立物件,這個方法聽起來不容易,但是在 http://www.javaworld.com中有一篇文章已經寫好了整個函式庫,因此 還算方便。 但是,整體說起來,二、三兩種方法都牽涉到自行處理.jar檔案的內容, 有一些不方便,這時候我們再回來看看第一種方法為什麼不行。我們知道, java在取得class資訊的時候,是透過classpath這個環境變數,然而,ClassLoader 使用classpath環境變數的時機跟一般想像有點不同,通常,java應用程式一啟動 就已經有一個系統的ClassLoader,這個ClassLoader會把目前環境的classpath 環境變數抄下來一份,之後要取得class資訊便會透過這份抄下來的classpath 取得。換言之,如果我們在程式中才動態的改變classpath這個環境變數, 那ClassLoader並不會紀錄到,因此來不及。這時應該怎麼辦呢?有一個 很方便的方法是自己實作ClassLoader,繼承URLClassLoader,並且在起始化 這個ClassLoader的時候利用ClassLoader的addURL這個函式將.jar檔案的路徑 全名紀錄進去,使用的格式必須是URL格式,例如file:c:/temp/test.jar,然後 在使用Class.forName這個函式的時候,指定使用我們自己實作的ClassLoader 就可以了。 改寫的ClassLoader如下: import java.net.*; public class JarClassLoader extends URLClassLoader { public JarClassLoader() { super(new URL[] {}); try{ addURL(new URL("file:F:/myproject/MMQtest/function_test/test.jar")); }catch(Exception e){System.out.println(e);} } public void addURL(URL url) { System.out.println("addURL:"+url); super.addURL(url); } public Class findClass(String name) throws ClassNotFoundException { return super.findClass(name); } }