摘要 eclipse豐富的客戶端平臺(tái)(rcp)正在快速地成為構(gòu)建胖客戶端應(yīng)用程序的框架選擇。本文將向你詳細(xì)介紹如何利用eclipse rcp進(jìn)行spring 。
  一. 引言
  盡管web 2.0和豐富的因特網(wǎng)應(yīng)用程序(ria)如今極為風(fēng)行,但是,當(dāng)你真正需要胖客戶端功能時(shí)構(gòu)建一個(gè)豐富的web前端可能并不真正滿足你的要求。
  但是,如果你確實(shí)想避開所謂ria狂熱而選擇一種實(shí)際的胖客戶端解決方案的話,那么你該怎樣做呢?回答是:你可以選擇一種豐富的客戶端平臺(tái)(rcp)來為你處理大多數(shù)的工作。實(shí)質(zhì)上,這種rcp概念為java桌面應(yīng)用程序世界提供了一種新型的框架。
  一個(gè)rcp提供了一個(gè)應(yīng)用程序的框架/外殼,還有一組基于模塊的api,你能夠基于這一外殼來構(gòu)建自己的應(yīng)用程序。這個(gè)rcp負(fù)責(zé)實(shí)現(xiàn)所有的繁重任務(wù),例如添加菜單,工具條,不同的視圖等等,而你就不必再重復(fù)工作。
  本文將引導(dǎo)你詳細(xì)地構(gòu)建一個(gè)胖客戶端接口以連接到在上一篇文章中構(gòu)建的服務(wù)器上。你將基于eclipse豐富的客戶端平臺(tái)來構(gòu)建胖客戶端,然后把eclipse rcp與spring集成到一起。
  【準(zhǔn)備工作】
  ·eclipse 3.1.2
  ·myeclipse 4.1.1
  ·java se 5
  ·一個(gè)servlet容器或j2ee服務(wù)器(本文使用的是tomcat 5.5+)
  ·spring 1.2+
  二. 為什么使用eclipse rcp?
  如今已經(jīng)有越來越多的應(yīng)用程序基于eclipse rcp進(jìn)行開發(fā)(當(dāng)然,還有eclipse厚實(shí)的開發(fā)背景),所以,我們可以安全地假定,與任何其它框架相比,這種框架已經(jīng)得到更為廣泛的測(cè)試。
  下面,讓我們開始。
  (一) 創(chuàng)建一個(gè)新的eclipse插件工程
  請(qǐng)按照下列步驟為你的豐富的客戶端應(yīng)用程序創(chuàng)建一個(gè)新的eclipse插件工程:
  1. 在eclipse中創(chuàng)建一個(gè)新的插件工程,并命名工程為eclipsetradeclient。把這個(gè)插件的應(yīng)用目標(biāo)定位在eclipse 3.1版本,并且確保點(diǎn)選了"create an osgi bundle manifest"(見圖1),并點(diǎn)擊next。

圖1."new plug-in project"對(duì)話框在eclipse中創(chuàng)建一個(gè)新的插件工程eclipsetradeclient。
  2. 在"plug-in content"屏幕上,保持默認(rèn)設(shè)置,但是確保選擇了"yes"-創(chuàng)建一個(gè)豐富的客戶端應(yīng)用程序(見圖2),并點(diǎn)擊next。

圖2.在"plug-in content"屏幕選擇創(chuàng)建一個(gè)豐富的客戶端應(yīng)用程序。
  ·至于模板,選擇"rcp application with a view",并點(diǎn)擊next。
  ·填寫顯示如圖3的rcp應(yīng)用程序?qū)傩裕Ⅻc(diǎn)擊finish。之后,你將被提示轉(zhuǎn)到"plug-in development"視圖下,并點(diǎn)擊yes。

圖3.rcp插件工程向?qū)ё詈蟮慕Y(jié)果屏幕
  ·現(xiàn)在,你已經(jīng)創(chuàng)建了你的工程,再打開plugin.xml。你將看到如下圖4所示的屏幕快照。

圖4.plugin.xml概要
  如果你是eclipse插件開發(fā)的新手,你可能經(jīng)常需要使用底部的plugin.xml選項(xiàng)卡。正如你從overview選項(xiàng)卡中所看到的,你可以運(yùn)行/調(diào)試你的eclipse豐富的客戶端應(yīng)用程序。
  ·展開eclipsetradeclient/src/eclipsetradeclient包來觀察eclipse的rcp向?qū)槟銊?chuàng)建的類。在eclipse編輯器中,點(diǎn)擊"all extensions"選項(xiàng)卡并且展開每一個(gè)頂級(jí)的結(jié)點(diǎn),如圖5所示。

圖5.eclipse生成的類及所有的擴(kuò)展
  請(qǐng)注意一下你的application類,perspective類和view類的擴(kuò)展入口。既然eclipse的豐富的客戶端平臺(tái)包括plugin.xml文件,所以你可以簡(jiǎn)單地添加新的組件-通過"extensions"選項(xiàng)卡中的"add..."按鈕來添加它們。
  (二) 重構(gòu)默認(rèn)的view類
  如你所見,eclipse向?qū)槟銊?chuàng)建了一個(gè)稱為view的類。并不是很有用,對(duì)嗎?請(qǐng)使用如下步驟來重構(gòu)默認(rèn)的視圖類:
  1. 讓我們重命名它-右擊package explorer中的view.java。轉(zhuǎn)到refactor->rename,輸入新名為explorerview并且點(diǎn)擊preview。在隨后彈出的面板上,你會(huì)看到perspective類被重構(gòu)-使用explorerview.id來代替view.id(見圖6)。點(diǎn)擊ok。

圖6.為explorerview重構(gòu)view類
  2. 遺憾的是,eclipse的重構(gòu)能力有點(diǎn)弱-特別與intellij作比較的話。對(duì)于象這樣的重構(gòu)來說,intellij將不僅按期望對(duì)類加以改變,而且它會(huì)把重構(gòu)應(yīng)用于你的.xml文件!這是非常有用的特征,特別是在一種spring/hibernate/xml配置操作比例極大的情況下。
  你必須手工地更新對(duì)plugin.xml的重構(gòu)。打開plugin.xml,并且點(diǎn)擊plugin.xml選項(xiàng)卡。找到相應(yīng)于view的擴(kuò)展,并且作如下更新:
name="explorerview"
class="eclipsetradeclient.explorerview"
id="eclipsetradeclient.explorerview">
  此后,進(jìn)行保存(見圖7)。

圖7.進(jìn)一步重構(gòu)-手工更新plugin.xml
  3. 對(duì)于這種簡(jiǎn)單的重構(gòu),情況就是這樣,對(duì)嗎?是的;但遺憾的是,你還沒有結(jié)束。打開類explorerview,改變靜態(tài)變量id-把它初始化為eclipsetradeclient.explorerview。這相應(yīng)于你剛才在plugin.xml中設(shè)置的id。
  4. 最后,你完成重構(gòu)。現(xiàn)在,讓我們測(cè)試一下是否一切改動(dòng)正常。切換回編輯器中的plugin.xml,并且點(diǎn)擊"overview"選項(xiàng)卡。點(diǎn)擊"launch an eclipse application",這應(yīng)該導(dǎo)致如圖8所示結(jié)果。

圖8.啟動(dòng)eclipse trade client程序
  5. 現(xiàn)在讓我們改變結(jié)點(diǎn)的名字。打開類explorerview。找到內(nèi)部類viewcontentprovider,并且改變方法"object getelements(object parent)",讓其返回一個(gè)字符串?dāng)?shù)組({"watch list","order history"})。

  三. 把spring remoting添加到你的應(yīng)用程序
  下面,我們把spring添加到你的eclipse豐富的客戶端以便它向前一篇文章中的stocktradeserver工程發(fā)出請(qǐng)求。
  首先,當(dāng)開發(fā)eclipse插件/rcp應(yīng)用程序時(shí),添加第三方庫的推薦的方法是通過另外一個(gè)插件。這樣做以后,你就不需要把這些第三方j(luò)ars添加到你創(chuàng)建的每個(gè)工程。而是,你僅建立你的插件/rcp工程和第三方庫工程之間的某種依賴性。首先,我們假定你熟悉eclipse的classloader。要點(diǎn)是,你必須采取一些額外的步驟來確保你的類在插件依賴性之間能夠彼此找到對(duì)方:
  1. 在eclipse中創(chuàng)建一個(gè)新的插件工程。并命名這個(gè)新工程為springclient。然后,設(shè)置如下值:
id = springclient
class = springclient.springclientplugin
  在填充plug-in屬性后,點(diǎn)擊finish。對(duì)于springclient插件工程,你不需要任何模板,因?yàn)槠渲饕康氖谴鎯?chǔ)spring庫和任何spring相關(guān)的服務(wù)類。
  2. 在你的解壓的spring-framework-1.2.8發(fā)行包中,你會(huì)找到下列jar文件:
  ·spring-aop.jar-在文件夾dist下
  ·spring-beans.jar-在文件夾dist下
  ·spring-context.jar-在文件夾dist下
  ·spring-core.jar-在文件夾dist下
  ·spring-remoting.jar-在文件夾dist下
  ·commons-logging.jar-在文件夾lib/jakarta-commons下
  ·log4j-1.2.13.jar-在文件夾lib/log4j下
  然后,把它們?nèi)繌?fù)制到你的springclient根目錄下。
  3. 在eclipse的包資源管理器中,右擊springclient以打開工程屬性。選擇"java build path",點(diǎn)擊"libraries"選項(xiàng)卡,并且把剛才你通過按鈕"add jars"添加的所有的那些jar文件加入。請(qǐng)確保你也導(dǎo)入了這些庫!點(diǎn)擊"order and export"選項(xiàng)卡,并且檢查所有的庫(見圖9)。通過這樣做,你就可以確保,當(dāng)你創(chuàng)建對(duì)springclient的一種工作依賴性時(shí),spring和log jars也是可用的。此后,點(diǎn)擊ok。

圖9.輸出第三方庫
  4. 現(xiàn)在,你要修改springclient的manifest。在包資源管理器中,展開springclient->meta-inf并且打開manifest.mf。點(diǎn)擊"runtime"選項(xiàng)卡并且點(diǎn)擊classpath部分的"add"。全選spring jars和logging jars并且點(diǎn)擊ok。現(xiàn)在,在"exported packages"節(jié)中,點(diǎn)擊add。選擇所有的包以便導(dǎo)出,并點(diǎn)擊ok(見圖10)。

圖10.把第三方庫添加到插件classpath并導(dǎo)出包
  我以前提及過,eclipse的classloader經(jīng)常引起問題。為了補(bǔ)救這一點(diǎn),你可以點(diǎn)擊manifest.mf選項(xiàng)卡并且添加下面一行:
  eclipse-buddypolicy: registered
  5. 現(xiàn)在,讓我們添加spring配置文件。在package explorer中,轉(zhuǎn)到src目錄,創(chuàng)建一個(gè)新文件applicationcontext.xml,并且加入下列內(nèi)容:

  在src目錄下,另外創(chuàng)建一個(gè)新文件beanreffactory.xml并且加入下列內(nèi)容:

  不必感到驚訝,這些配置文件與你對(duì)stocktradeserver工程進(jìn)行單元測(cè)試時(shí)使用的spring配置文件是相同的,除了你重命名了applicationcontext.xml以外。
  6. 為了簡(jiǎn)化問題,你可以把類從stocktradeserver工程復(fù)制到springclient的src目錄下。在springclient的src目錄下面,創(chuàng)建包stephenlum.services.stock和stephenlum.services.stock.dto。
  如果你還沒有準(zhǔn)備好,你可以下載本文源碼或參考我的前一篇文章并且復(fù)制stephenlum.services.stock下的類stockservice.java。然后,復(fù)制在stephenlum.services.stock.dto下的類stockdto.java(見圖11)。

圖11.完成上面操作后的package explorer看上去的樣子
  7. 把代碼添加到類springclientplugin以裝載spring的應(yīng)用程序上下文。注意,你要把該代碼添加到構(gòu)造器中。下面是新的springclientplugin類:
package springclient;
import org.eclipse.jface.resource.imagedescriptor;
import org.eclipse.ui.plugin.abstractuiplugin;
import org.osgi.framework.bundlecontext;
import org.springframework.beans.factory.beanfactory;
import org.springframework.beans.factory.access.beanfactorylocator;
import org.springframework.beans.factory.access.beanfactoryreference;
import org.springframework.beans.factory.access.singletonbeanfactorylocator;
/**
*應(yīng)用于桌面的主插件類。
*/
public class springclientplugin extends abstractuiplugin {
 private beanfactory beanfactory;
 //共享實(shí)例.
 private static springclientplugin plugin;
 /**
 *構(gòu)造器.
 */
 public springclientplugin() {
  plugin = this;
  beanfactorylocator beanfactorylocator = singletonbeanfactorylocator.getinstance();
  beanfactoryreference beanfactoryreference = beanfactorylocator.usebeanfactory("ctx");
  beanfactory = beanfactoryreference.getfactory();
 }
 /**
 *在插件激活時(shí)調(diào)用這個(gè)方法
 */
 public void start(bundlecontext context) throws exception {
  super.start(context);
 }
 /**
 *當(dāng)停止插件時(shí),調(diào)用這個(gè)方法
 */
 public void stop(bundlecontext context) throws exception {
  super.stop(context);
  plugin = null;
 }
 /**
 *返回共享實(shí)例.
 */
 public static springclientplugin getdefault() {
  return plugin;
 }
 /**
 *返回在給定的插件相對(duì)路徑下的圖像文件的一個(gè)圖像描述符
 * @param path-路徑
 * @返回圖像描述符
 */
 public static imagedescriptor getimagedescriptor(string path) {
  return abstractuiplugin.imagedescriptorfromplugin("springclient", path);
 }
 public beanfactory getbeanfactory() {
  return beanfactory;
 }
}
  8. 最后,添加依賴性以實(shí)現(xiàn)工程eclipsetradeclient依賴于你的新插件工程springclient。在工程eclipsetradeclient中,打開plugin.xml并且點(diǎn)擊"dependencies"選項(xiàng)卡。在"required plug-ins"節(jié)中,點(diǎn)擊add,選擇"springclient(1.0.0)",并且點(diǎn)擊ok(見圖12)。

圖12.把springclient添加為一個(gè)要求的插件
  現(xiàn)在,你必須在eclipsetradeclient manifest文件中添加與eclipse的伙伴策略相關(guān)的內(nèi)容。在文件plugin.xml中,點(diǎn)擊manifest.mf選項(xiàng)卡并且添加下列入口:
  eclipse-registerbuddy: springclient  四. 創(chuàng)建一個(gè)新的watchlistview
  現(xiàn)在,你可以開始創(chuàng)建你自己的視圖類了。首先,你要?jiǎng)?chuàng)建一個(gè)watchlistview,它將向應(yīng)用程序服務(wù)器的stockdataservice發(fā)出一個(gè)請(qǐng)求:
  1. 在plugin.xml中,轉(zhuǎn)到extensions選項(xiàng)卡。
  2. 在all extensions樹中選擇org.eclipse.ui.views,然后點(diǎn)擊add。
  3. 隨后出現(xiàn)一個(gè)新的對(duì)話框窗口。在extension points樹中滾動(dòng)并且選擇org.eclipse.ui.views。在相應(yīng)于org.eclipse.ui.views的可用模板中,選擇sampleview,然后點(diǎn)擊next(見圖13)。

圖13.新的擴(kuò)展對(duì)話框
  4. 在"main view settings"窗口中,填寫如下內(nèi)容:
java package name = eclipsetradeclient.views.watchlist
view class name = watchlistview
view name = watch list view
view category id = eclipsetradeclient
view category name = watchlist category
  讓"table viewer"保持選擇狀態(tài)并且點(diǎn)選"add the view to the resource perspective checked"(見圖14)。點(diǎn)擊next。

圖14.針對(duì)于watch list視圖設(shè)置"main view settings"
  5. 在"view features"下,保持默認(rèn)設(shè)置并且點(diǎn)擊finish。
  6. 現(xiàn)在,你會(huì)在plugin.xml的"all extensions"選項(xiàng)卡中看到新的"view and category"。
  7. 現(xiàn)在,你可以開始編寫你的watch list視圖了。這個(gè)觀察列表是一個(gè)表格,因此首先要為此表實(shí)現(xiàn)接口itablelabelprovider。在包eclipsetradeclient.views.watchlist下創(chuàng)建一個(gè)新類watchlisttablelabelprovider。你可以把一個(gè)itablelabelprovider當(dāng)作是jface的等價(jià)物-swing中的tablecellrenderer。下面是watchlisttablelabelprovider的代碼部分:
package eclipsetradeclient.views.watchlist;
import java.text.numberformat;
import org.eclipse.jface.viewers.itablelabelprovider;
import org.eclipse.jface.viewers.labelprovider;
import org.eclipse.swt.graphics.image;
import stephenlum.services.stock.dto.stockdto;
public class watchlisttablelabelprovider extends labelprovider implements
itablelabelprovider {
 private static numberformat numberformat = numberformat.getinstance();
 public image getcolumnimage(object element, int columnindex) {
  return null;
 }
 public string getcolumntext(object element, int columnindex) {
  if (element != null) {
   switch (columnindex) {
    case 0:
     return ((stockdto) element).gettickersymbol();
    case 1:
     return ((stockdto) element).getlasttrade().tostring();
    case 2:
     return numberformat.format(((stockdto) element).getvolume());
    case 3:
     return ((stockdto) element).getdaysrange();
    case 4:
     return numberformat.format(((stockdto) element).getavgvol());
    case 5:
     return ((stockdto) element).getdaysrange();
    case 6:
     return ((stockdto) element).getfiftytwoweekrange();
    case 7:
     return ((stockdto) element).getmarketcap(); 
   } 
  }
  return "";
 }
}
  8. 最后,你把你的watchlistview添加到perspective類。在package explorer中打開類perspective并且作如下修改以便watchlistview將出現(xiàn)于該頁面的底部:
package eclipsetradeclient;
import org.eclipse.ui.ipagelayout;
import org.eclipse.ui.iperspectivefactory;
import org.eclipse.ui.ifolderlayout;
import eclipsetradeclient.views.watchlistview;
public class perspective implements iperspectivefactory {
 public void createinitiallayout(ipagelayout layout) {
  string editorarea = layout.geteditorarea();
  layout.seteditorareavisible(false);
  layout.setfixed(false);
  layout.addstandaloneview(explorerview.id, 
    false, 
    ipagelayout.left, 
    0.25f, 
    editorarea);
  ifolderlayout topleft = layout.createfolder("top", 
    ipagelayout.top, 
    0.50f, 
    editorarea);
  layout.addview(watchlistview.id,ipagelayout.bottom, 0.25f,editorarea);
 }
}       
  9. 現(xiàn)在,你可以在類watchlistview中進(jìn)行添加。我盡量保持模板生成的代碼不動(dòng)以便于你可以自由地添加你的代碼。實(shí)質(zhì)上,你是在添加一個(gè)表格-它將顯示包含在一個(gè)類stockdto實(shí)例中的所有信息。因此,表格中的列也是基于stockdto的成員。我已經(jīng)重命名了兩個(gè)生成的action-現(xiàn)在action1能夠從stocktradeserver中取回股票的列表并且在表格中顯示它們,而action2從表格中刪除所有元素(請(qǐng)參考源碼中的列表1.eclipsetradeclient.views.watchlist)。  五. 運(yùn)行應(yīng)用程序
  現(xiàn)在,你可以運(yùn)行你的應(yīng)用程序了。如果還沒有準(zhǔn)備好的話,你可以把stocktradeserver工程按如下步驟導(dǎo)入到eclipse:
  1. 在eclipse中,點(diǎn)擊工具欄按鈕"deploy myeclipse j2ee project to server"(見圖15)。

圖15.發(fā)布myeclipse j2ee服務(wù)器按鈕
  確保在列表下的工程是stocktradeserver。點(diǎn)擊add,選擇tomcat 5作為你的服務(wù)器,并且點(diǎn)擊finish。當(dāng)你看到一條消息"successfully deployed"時(shí),點(diǎn)擊ok(見圖16)。

圖16.stocktradeserver被成功發(fā)布
  現(xiàn)在,啟動(dòng)tomcat服務(wù)器(見圖17)。tomcat應(yīng)該會(huì)成功地啟動(dòng)。

圖17.通過myeclipse插件啟動(dòng)tomcat
  2. 啟動(dòng)"eclipse rich client"。打開eclipsetradeclient's plugin.xml文件,點(diǎn)擊"overview"選項(xiàng)卡,并且點(diǎn)擊"launch an eclipse application"。當(dāng)應(yīng)用程序啟動(dòng)時(shí),按下圖18中的紅色按鈕以得到一個(gè)股票列表。這一行為將使用spring httpinvoker從應(yīng)用程序服務(wù)器取回股票列表。你可以按下紅色圓圈右邊的按鈕來清除股票列表(見圖18)。

圖18.eclipsetradeclient成功運(yùn)行
  一切順利!你已經(jīng)成功構(gòu)建了一個(gè)小型的eclipse豐富的客戶端程序并且使用spring remoting技術(shù)把它連接到一個(gè)應(yīng)用程序服務(wù)器上。
  六. 小結(jié)
  總之,基于eclipse rcp構(gòu)建你的胖客戶端程序?qū)?huì)大大減少構(gòu)建這種程序的gui框架所需的繁重代碼。另外,通過把spring remoting用作客戶端/服務(wù)器通訊機(jī)制還允許你輕松地實(shí)現(xiàn)協(xié)議的切換,同時(shí)還提供其它所有在服務(wù)器端的spring優(yōu)點(diǎn)。