

摘 要:本文首先研究了Java語言的動態加載方式和實現插件化的方法, 并以支持不同音頻格式的音樂播放器為例展示了Java插件的開發步驟。本文所展示的Java插件的開發方法和步驟具有一般性指導意義,可以很好地解決新功能植入或著功能模塊定制問題。
關鍵詞:動態加載;Java 插件;功能植入;模塊定制
1 概述
軟件項目開發經常會遇到這樣的情況發生,就是當項目開發結束并交付使用后,需要增加一些新功能。我們通常希望在不改變原有應用程序代碼的前提下,將新增加的功能植入到系統中。
還有一種情況就是針對同一個功能模塊,不同用戶會要求不同的具體實現。例如,呼叫中心坐席軟件中的來電彈屏功能,不同用戶可能要求展示客戶信息的方式不一樣,信息來源不一樣,有的甚至要求完成一些額外的功能。這就是所謂的功能模塊定制。
不管是新功能植入,還是功能模塊定制,我們都可以使用插件化技術來解決。新增加的功能模塊,或者被定制的功能模塊,被
稱為插件。插件化技術帶來的好處直接體現為降低了模塊之間的耦合性,便于各個模塊的獨立維護,方便和加快整個項目的維護更新。
Java語言的動態加載技術完美的支持了插件化開發。本文首先研究了Java語言的動態加載方式和實現插件化的方法,并以支持不同音頻格式的音樂播放器為例展示了Java插件的開發步驟。
2 Java動態加載并實例化的幾種方式
Java程序運行時是需要把類加載到內存的。Java類的加載方式有兩種:隱式加載和顯式加載。隱式加載是通過new的方式,如:Person person=new Person(),在類初始化時由Java虛擬機根據相應的類加載器(ClassLoader)將類載入[1]。顯式加載則是程序員在代碼中顯式地利用某個類加載器將類載入。顯示加載并實例化類又可以通過下面兩種方法:
2.1 使用Class類[1]
在Java中通過Class類的靜態方法forName()可以動態加載一個指定的類(需要提供類的完整名字),然后調用newInstance()就可以構造一個該類的實例,例如:String className = "com.abc.Person";
Person person=Class.forName(className).newInstance();
這個功能對于相同接口的不同實現具有重大意義。我們可以定義一個接口,然后提供好幾個該接口的實現類,用戶通過修改配置文件來指定實際使用哪一個實現類,然后在源碼中讀取配置文件,就可以構造一個指定實現類的實例而不用每次修改源碼。通過這種方法,系統只要關心接口的定義,用戶只要進行配置文件的設置,就完成了同一功能不同實現的任意切換。
2.2 使用ClassLoader
這種方法需要首先獲得ClassLoader,然后通過ClassLoader的loadClass ()方法動態加載一個指定的類。Java提供了以下三種途徑得到ClassLoader:
·使用系統類加載器:ClassLoader.getSystemClassLoader();
·使用當前類加載器:this.getClass.getClassLoader();
·使用當前線程類加載器:Thread.currentThread().getContextClassLoader();
下面的代碼片段展示了如何使用系統類加載器裝載并實例化的過程:
ClassLoader cloader = ClassLoader.getSystemClassLoader();
Class cls = cloader.loadClass("com.abc.Person");
Person person = (Person)cls.newInstance();
需要注意的是:系統類加載器處理-classpath下的類加載工作,它適合于簡單的命令行應用,如果是EJB, 或java Web應用,請使用其他的類加載器。
3 Java 插件的開發(Java Plugin development)
下面考慮這樣一種應用場景:現有用Java語言編寫的主程序,要求第三方開發人員編寫擴展功能。約定第三方開發人員開發的類必須包含一個實現某已知接口的類,名稱不限。要求第三方開發的類打成 jar包,與主程序分開發布。jar包放在主程序安裝目錄下的plugins子目錄下,主程序啟動時動態加載 jar 包中的類。
這是典型的Java插件技術在實際應用中的體現。為了開發java插件,java提供了URLClassLoader解決從jar文件中動態加載類。 URLClassLoader是ClassLoader的子類,它用于從指定的jar文件和目錄的URL搜索路徑加載類和資源,即,通過URLClassLoader就可以加載指定jar中的類到內存中[2]。下面的代碼片段說明了這一過程:
// 動態加載jar包
String url_str = "file:C:\PluginDemo\plugins\WMAPlayer.jar";
URL[] urls = new URL[1];
urls[0] = new URL(url_str);
URLClassLoader loader = new URLClassLoader(urls);
//從加載器中加載插件類,然后實例出一個對象
Class<?> c = loader.loadClass("com.abc.plugin.WMAPlayer");
PlayerIface ?instance = (PlayerIface) c.newInstance();
在插件開發過程中,首先由開發人員編寫系統框架,并預先定義好系統的擴展接口。插件由其他開發人員(或第三方開發人員)根據預先定義好的接口編寫系統的擴展功能模塊。插件以獨立的jar 包形式出現。對系統而言,它并不知道也不可能知道插件的具體實現,它所做的就是為插件預定義好接口。系統啟動的時候根據配置文件尋找插件,并根據預定義接口將插件掛接到系統中。插件的基本結構如圖1所示。
圖中,MANIFEST.MF是每個插件的描述文件,應用程序加載插件時首先解析每個插件的描述文件,然后在應用程序中動態加載類并實例化,最后調用類的方法。具體而言,將每個插件打成 jar包,里面包含一個實現預定義接口的類,在jar的MANIFEST.MF文件中給出該類的全路徑。在應用程序中使用URLCLassLoader裝載jar包,使用java.util.jar.JarFile和java.util.jar.Manifest解析jar包和MANIFEST.MF文件[3],獲得插件類以后用URLCLassLoader的loadClass()方法裝載類,最后使用newInstance()創建該類的實例。
圖1 ?插件的基本結構
下面以音樂播放器為例,具體展示Java插件的開發過程。本例將支持不同音頻格式的音樂播放器做成不同的插件。
第一步:預定義系統的擴展接口。
package com.abc.demo;
public interface PlayerIface{
public void load(String filename);
public void play();
public void stop();
public String getSupportedFormat();
}
第二步:編寫實現擴展接口的插件類。
下面是MP3播放器插件類,插件類實現了擴展接口PlayerIface,并且必須擁有不帶參數的構造方法,以便實例化時被調用。用同樣的方法可以編寫其他音頻格式的播放器。
package com.abc.plugin;
public class MP3Player implements PlayerIface{
//實現接口中的方法,代碼略。
}
第三步:編寫自己的MANIFEST.MF文件,其中有一個Plugin-Class屬性,指定了插件類的完整類名。文件內容如下:
Manifest-Version: 1.0
Plugin-Class: com.abc.plugin.MP3Player
第四步:將插件類com.abc.plugin. MP3Player打成MP3Player.jar包,打包時注意將上述MANIFEST.MF文件打入包中。
第五步:將jar包發布在主程序的安裝目錄的子目錄中,一般是plugins子目錄。
如圖2所示。
圖2 ?插件的發布目錄
第六步:主程序啟動時在plugins子目錄中列舉所有插件,針對每個jar包解析其中的MANIFEST文件,提取Plugin-Class屬性的值,獲取插件類的完整類名。用URLCLassLoader的loadClass()方法裝載該類,最后使用newInstance()創建該類的實例。
4 結束語
本文所展示的Java插件的開發方法和步驟具有一般性指導意義,可以很好地解決新功能植入或功能模塊定制。作者最近在一個呼叫中心的坐席軟件中就是利用Java插件技術解決了來電彈屏定制問題。正因為插件技術降低了模塊之間的耦合性,便于各個模塊的獨立維護,方便和加快整個項目的維護更新,可以在不修改原有應用程序代碼的情況下植入新的功能,或者完成某個功能模塊的定制,從而在軟件項目開發中得到廣泛的應用。
參考文獻:
[1]Java:靜態和動態類加載[EB/OL].http://javaj2ee.com/node/14.
[2]動態類加載[EB/OL]. https://en.wikibooks.org/wiki/Java_Programming/
Reflection/Dynamic_Class_Loading.
[3]Java程序:從jar 包讀取manifest文件[EB/OL], https://blogs.oracle.com/jmxetc/entry/a_java_program_that_reads.
作者簡介:
錢宇虹(1967-),女,計算機科學碩士,副教授,研究領域:軟件開發與應用、軟件工程、軟件測試技術。