利用動(dòng)態(tài)類加載技術(shù)調(diào)式ECLIPSE插件
eclipse平臺(tái)提供runtime方式調(diào)試插件和RCP項(xiàng)目,但隨著插件項(xiàng)目越寫約復(fù)雜,啟動(dòng)時(shí)間也越來(lái)越長(zhǎng),特別是集成了諸如 Hibernate和Spring之類的容器級(jí)框架的時(shí)候。僅僅為了調(diào)試代碼中一些瑣碎的片段而頻繁的重啟項(xiàng)目實(shí)在是一件異常煩人的工作。
即使重啟了項(xiàng)目也許還沒完。為了使項(xiàng)目處于某個(gè)特定的狀態(tài)下以方便測(cè)試,每次都要重新操作一遍前面業(yè)務(wù)流程,這同樣是十分令人厭倦的。
eclispe使用了OGSI作為微內(nèi)核,引入了一些動(dòng)態(tài)特性。但是OSGI的動(dòng)態(tài)特性是在保持平臺(tái)運(yùn)行的情況下動(dòng)態(tài)更新Bundle,也就是說(shuō)需要重啟插件才能完成動(dòng)態(tài)加載的過(guò)程。有沒有一些更加細(xì)粒度的動(dòng)態(tài)載入方案呢?
在Tomcat下開發(fā)過(guò)web項(xiàng)目的人都知道,使用調(diào)試模式來(lái)部署項(xiàng)目即”熱部署”可以實(shí)現(xiàn)動(dòng)態(tài)載入class文件,讓程序員得以動(dòng)態(tài)調(diào)試項(xiàng)目。今天向大家提供的這個(gè)jar包使得這種效果可以在eclipse runtime上實(shí)現(xiàn)。
這是我在自己的插件平臺(tái)項(xiàng)目——SCOOP框架中使用的幾個(gè)包。
它可以非常好的解決動(dòng)態(tài)類載入的問題,包括內(nèi)部類的動(dòng)態(tài)載入都可以很好解決。
其他的幾個(gè)包還進(jìn)行了以下嘗試
1. 使用元數(shù)據(jù)標(biāo)注的辦法解決SWT UI 線程的種種問題
2. 還提供了eclipse流程框架的簡(jiǎn)單實(shí)現(xiàn),以規(guī)范插件開發(fā)。特別是提出了一個(gè)面向業(yè)務(wù)而不是面向技術(shù)的工作流概念,使得編碼粒度變大,并得以提高效率。另外這個(gè)簡(jiǎn)單的流程框架還將前面的兩種機(jī)制很好的結(jié)合起來(lái),并且可以和eclipse平臺(tái)的一些復(fù)雜機(jī)制解耦,為復(fù)雜流程的開發(fā)測(cè)試提供了方便。為將來(lái)實(shí)現(xiàn)自定義腳本語(yǔ)言(比如某個(gè)類似于BPEL的工作流語(yǔ)言)開發(fā)eclise插件項(xiàng)目甚至使用圖形化的開發(fā)奠定了一定的基礎(chǔ)。
我給出了一個(gè)完整的示例——JAXB插件。很多框架同jaxb一樣提供了code generation工具,可以在這個(gè)例子的基礎(chǔ)上經(jīng)過(guò)簡(jiǎn)單修改為這些框架提供插件,比如CXF插件、AXIS2插件等等。
動(dòng)態(tài)類載入編碼原則
使用動(dòng)態(tài)類載入機(jī)制來(lái)進(jìn)行調(diào)試在編碼上有一定限制。
首先是要進(jìn)行動(dòng)態(tài)載入的實(shí)例不要在非動(dòng)態(tài)域進(jìn)行引用。只有這樣,當(dāng)一個(gè)流程結(jié)束時(shí)此實(shí)例才會(huì)在jvm中得以釋放。當(dāng)然,非要在其他地方進(jìn)行引用從而長(zhǎng)久的在運(yùn)行時(shí)保持這個(gè)實(shí)例也是有解決方案的(可以使用代理類技術(shù)來(lái)實(shí)現(xiàn),具體解決辦法不在本文之內(nèi))。
其次是進(jìn)行接口同實(shí)現(xiàn)類的分離或父類同子類的分離,以隔離不同的class load scope。接口由父類載入器載入,不同的實(shí)現(xiàn)(比如修改后的實(shí)現(xiàn))由不同的子類載入器載入。使得最終同一個(gè)類型由同一個(gè)類載入器載入,這樣才能符合jvm的類載入規(guī)范。在父類載入域的父類型的方法的參數(shù)類型及返回值類型也不能在動(dòng)態(tài)域中。
最后,那些注冊(cè)在擴(kuò)展點(diǎn)上的類如ActionDelegate和WorkbenchPart等是不能夠動(dòng)態(tài)載入的,他們必須由eclipse提供的類載入器載入(平臺(tái)會(huì)自動(dòng)載入并管理其生命周期)。如果需要讓這些類也動(dòng)態(tài)載入,就需要在平臺(tái)提供的動(dòng)態(tài)注冊(cè)機(jī)制基礎(chǔ)上使用代理或像EJB2.0一樣使用侵入式編譯來(lái)實(shí)現(xiàn)代理機(jī)制,這個(gè)話題同樣不在本文之內(nèi)。
下面就舉例說(shuō)明使用方法
因?yàn)闀r(shí)間有限我無(wú)法詳盡的完成此文,請(qǐng)感興趣的朋友自己閱讀示例源碼。
1. 幾種動(dòng)態(tài)類載入的辦法
(1) 使用手動(dòng)編碼進(jìn)行類載入
因?yàn)闀?huì)使二次開發(fā)人員產(chǎn)生迷惑,故未舉例
(2) 在調(diào)試時(shí)使用spring文件配置動(dòng)態(tài)類載入域
Spring配置文件同樣也是動(dòng)態(tài)的,而且會(huì)使調(diào)試開發(fā)工作變得更加清晰,推薦使用
(3) 使用Flow框架來(lái)進(jìn)行動(dòng)態(tài)類載入
2. 使用元數(shù)據(jù)標(biāo)注解決UI線程訪問的問題
使用三個(gè)元數(shù)據(jù)及動(dòng)態(tài)代理類解決SWT及Eclipse的線程問題。
3. 同eclipse內(nèi)部機(jī)制解耦以方便開發(fā)和測(cè)試
開發(fā)插件項(xiàng)目很多時(shí)候需要實(shí)現(xiàn)Eclipse內(nèi)部的一些回調(diào)接口來(lái)實(shí)現(xiàn)功能,這對(duì)程序員的水平是一種考驗(yàn),也使得插件開發(fā)工作更加復(fù)雜化。比如在實(shí)現(xiàn)progressMonitor的時(shí)候,就需要實(shí)現(xiàn)它的回調(diào)接口,將業(yè)務(wù)邏輯放置在其指定的回調(diào)接口——runable接口來(lái)實(shí)現(xiàn),這是非常不方便的。我們需要是一種可以提供功能的工具類,像調(diào)用一個(gè)普通javaBean一樣來(lái)調(diào)用它,而不是將我們的業(yè)務(wù)代碼變形撕碎去融入到Eclipse的種種機(jī)制中去。
這樣做的另外一個(gè)壞處就是很難進(jìn)行單元測(cè)試,比如脫離eclipse平臺(tái),使用一些mock類來(lái)進(jìn)行簡(jiǎn)單有效的單元測(cè)試。
我在這方面也進(jìn)行了一些嘗試——”反客為主”,將必須遵守eclipse的回調(diào)要求變?yōu)楸仨氉袷貥I(yè)務(wù)開發(fā)簡(jiǎn)單快捷的要求。同樣是在progressMonitor上面,使用工廠類來(lái)創(chuàng)建delegate,然后可以在回調(diào)機(jī)制的外部向調(diào)用javaBean一樣來(lái)使用平臺(tái)給我們提供的這個(gè)功能。
這種嘗試是有一定難度的,要使用到不同的設(shè)計(jì)模式,處理各種線程問題。更重要的一點(diǎn)是eclipse平臺(tái)本身有這樣的潛力,它也在等待著我們這樣做。
4. 使用Flow規(guī)范插件項(xiàng)目開發(fā)
我的scoop項(xiàng)目最終擱淺,到最后我發(fā)現(xiàn)實(shí)現(xiàn)它已經(jīng)超出了我當(dāng)時(shí)的能力。我原本是想開發(fā)一個(gè)統(tǒng)一的插件開發(fā)及部署平臺(tái)。這樣很多中小軟件企業(yè),特別是像我原來(lái)工作的那家公司就可以擁有自己的eclipse插件集,以適應(yīng)自己特殊的要求。我還想提供一套面向插件開發(fā)業(yè)務(wù)的接口,而不再面向技術(shù)也屏蔽各種技術(shù)細(xì)節(jié),使得可以非常方便的擴(kuò)展、修改甚至移植插件。我只是心里有想念就去實(shí)現(xiàn)而已,當(dāng)最終認(rèn)識(shí)到它有多么困難的時(shí)候不得不放棄了。最后雖然失敗了,但并不覺得氣餒。因?yàn)橹懒艘Φ姆较颍瑫r(shí)也積累了豐富的經(jīng)驗(yàn)。最后就將這個(gè)jar包命名為SCOOP已示紀(jì)念。
演示文檔及框架JAR包如下
/Files/jonenine/Eclipse_Dynamic_Classload.rar