Java ClassLoader 實(shí)現(xiàn)程序的擴(kuò)展性
今天在完成一個(gè)功能的時(shí)候,使用了ServiceLocate模式,
通過這個(gè)模式,在程序中可以自由的加載其他成員實(shí)現(xiàn)的功能模塊
具體的做法:
1)定義標(biāo)準(zhǔn)的服務(wù)接口。
2)定義描述實(shí)現(xiàn)服務(wù)接口的xml文件。
3)程序讀取該xml文件,使用Class.newInstance()實(shí)例化具體的服務(wù)對(duì)象。
4)建立一個(gè)特定服務(wù)和特定服務(wù)實(shí)現(xiàn)的對(duì)應(yīng)的HashMap對(duì)象。完成注冊(cè)任務(wù)。
5)主程序中根據(jù)具體的服務(wù)從HashMap中取得具體的對(duì)象進(jìn)行服務(wù)。
這個(gè)方法還不錯(cuò),可以完成基于Interface的開發(fā)要求,利于Test和程序的拓展性。
有新的要求出現(xiàn)后,只需要添加xml中的元素和具體的實(shí)現(xiàn)類就可以了。
接下來,繼續(xù)想。又發(fā)現(xiàn)了一些問題:
1)xml是和程序一起發(fā)布的,如果用戶隨意改動(dòng)了。很明顯程序會(huì)崩潰。
解決方法:xml放在jar包中,使用getClass().getResourceAsStream(String name)
自己加載進(jìn)來。用戶完全不知道具體的情況。
2)如果把xml放在了jar包中“藏起來”,實(shí)際上原來帶來的動(dòng)態(tài)擴(kuò)展的特性,
也就沒有那么明顯了。如何解決呢
細(xì)細(xì)想來,這個(gè)問題的關(guān)鍵在于,所有的服務(wù)實(shí)例對(duì)象的創(chuàng)建和注冊(cè)都是在
主控程序中通過xml來完成的。如果可以把這個(gè)注冊(cè)和實(shí)例化的過程從主控程序
中分離出來,通過每個(gè)服務(wù)實(shí)例對(duì)象自動(dòng)注冊(cè)來完成,那么才算是真正的可拓展的。
如果需要完成新的功能,只需要把新的服務(wù)對(duì)象Class發(fā)布,重新運(yùn)行主控程序就會(huì)實(shí)現(xiàn)新的功能。(看起來就和Eclipse一樣)
這真是一個(gè)不錯(cuò)的想法,但是怎么做呢?
看看Eclipse如何做的。
http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
首先要有一個(gè)規(guī)定的plugin deploy目錄,這樣主程序才知道從哪里加載。
要有一個(gè)plugin.xml文件描述這個(gè)plugin.這著文件中有屬性:class="foo.bar.Plugin">
看上去和我們原來做的方式一樣啊。但是它是如何把這個(gè)目錄下的plugin都加載的呢?
(我沒有看過Eclipse的源碼,不知道他是怎么寫的)
再想想,其實(shí)主要要解決的問題是不通過主框架程序注冊(cè)服務(wù)實(shí)現(xiàn),
應(yīng)該由服務(wù)程序自己注冊(cè)上來。按照這個(gè)思路,我想有兩種解決方案。
1)服務(wù)接口添加registerService 方法。
* 在jar的METATINFO文件中定義類名。
* 從特定的目錄中讀取jar/class文件。
* 通過URLClassLoader.newInstance()加載
* 加載后把ServiceLoader作為參數(shù)出入 service.registerSevice()中
* service對(duì)象完成自己的注冊(cè)。
2)服務(wù)添加static代碼端在類被實(shí)例化的時(shí)候自動(dòng)完成注冊(cè)。
* 加載之前和上一個(gè)方法一樣。
* 對(duì)SeviceLoader對(duì)象實(shí)現(xiàn)為單態(tài)的模式。提供靜態(tài)的注冊(cè)方法。
* 在servie對(duì)象中實(shí)現(xiàn)如下的代碼段完成自動(dòng)注冊(cè)。
static
{
ServiceLoader.registerService(new service());
}
這樣看來總算OK了吧。