摘要 Eclipse豐富的客戶端平台(RCP)正在快速地成為構建胖客戶端應用程序的框架選擇。本文將向你詳細介紹如何利用Eclipse RCP進行Spring Web開發。
一. 引言
盡管Web 2.0和豐富的因特網應用程序(RIA)如今極為風行,但是,當你真正需要胖客戶端功能時構建一個豐富的Web前端可能並不真正滿足你的要求。
但是,如果你確實想避開所謂RIA狂熱而選擇一種實際的胖客戶端解決方案的話,那麼你該怎樣做呢?回答是:你可以選擇一種豐富的客戶端平台(RCP)來為你處理大多數的工作。實質上,這種RCP概念為Java桌面應用程序世界提供了一種新型的框架。
一個RCP提供了一個應用程序的框架/外殼,還有一組基於模塊的API,你能夠基於這一外殼來構建自己的應用程序。這個RCP負責實現所有的繁重任務,例如添加菜單,工具條,不同的視圖等等,而你就不必再重復工作。
本文將引導你詳細地構建一個胖客戶端接口以連接到在上一篇文章中構建的服務器上。你將基於Eclipse豐富的客戶端平台來構建胖客戶端,然後把Eclipse RCP與Spring集成到一起。
【准備工作】
·Eclipse 3.1.2
·MyEclipse 4.1.1
·Java SE 5
·一個Servlet容器或J2EE服務器(本文使用的是Tomcat 5.5+)
·Spring 1.2+
二. 為什麼使用Eclipse RCP?
如今已經有越來越多的應用程序基於Eclipse RCP進行開發(當然,還有Eclipse厚實的開發背景),所以,我們可以安全地假定,與任何其它框架相比,這種框架已經得到更為廣泛的測試。
下面,讓我們開始。
(一) 創建一個新的Eclipse插件工程
請按照下列步驟為你的豐富的客戶端應用程序創建一個新的Eclipse插件工程:
1. 在Eclipse中創建一個新的插件工程,並命名工程為EclipseTradeClient。把這個插件的應用目標定位在Eclipse 3.1版本,並且確保點選了"Create an OSGi bundle manifest"(見圖1),並點擊Next。
圖1."New Plug-in Project"對話框在Eclipse中創建一個新的插件工程EclipseTradeClient。
2. 在"Plug-in Content"屏幕上,保持默認設置,但是確保選擇了"Yes"-創建一個豐富的客戶端應用程序(見圖2),並點擊Next。
圖2.在"Plug-in Content"屏幕選擇創建一個豐富的客戶端應用程序。
·至於模板,選擇"RCP application with a view",並點擊Next。
·填寫顯示如圖3的RCP應用程序屬性,並點擊Finish。之後,你將被提示轉到"Plug-in Development"視圖下,並點擊Yes。
圖3.RCP插件工程向導最後的結果屏幕
·現在,你已經創建了你的工程,再打開plugin.xml。你將看到如下圖4所示的屏幕快照。
圖4.Plugin.XML概要
如果你是Eclipse插件開發的新手,你可能經常需要使用底部的plugin.xml選項卡。正如你從Overview選項卡中所看到的,你可以運行/調試你的Eclipse豐富的客戶端應用程序。
展開EclipseTradeClient/src/eclipseTradeClient包來觀察Eclipse的RCP向導為你創建的類。在Eclipse編輯器中,點擊"All Extensions"選項卡並且展開每一個頂級的結點,如圖5所示。
圖5.Eclipse生成的類及所有的擴展
請注意一下你的Application類,Perspective類和View類的擴展入口。既然Eclipse的豐富的客戶端平台包括plugin.xml文件,所以你可以簡單地添加新的組件-通過"Extensions"選項卡中的"Add..."按鈕來添加它們。
(二) 重構默認的View類
如你所見,Eclipse向導為你創建了一個稱為View的類。並不是很有用,對嗎?請使用如下步驟來重構默認的視圖類:
1. 讓我們重命名它-右擊Package Explorer中的View.java。轉到Refactor->Rename,輸入新名為ExplorerView並且點擊Preview。在隨後彈出的面板上,你會看到Perspective類被重構-使用ExplorerView.ID來代替View.ID(見圖6)。點擊OK。
圖6.為ExplorerView重構View類
2. 遺憾的是,Eclipse的重構能力有點弱-特別與IntelliJ作比較的話。對於象這樣的重構來說,IntelliJ將不僅按期望對類加以改變,而且它會把重構應用於你的.xml文件!這是非常有用的特征,特別是在一種Spring/Hibernate/XML配置操作比例極大的情況下。
你必須手工地更新對plugin.xml的重構。打開plugin.xml,並且點擊plugin.xml選項卡。找到相應於View的擴展,並且作如下更新:
name="ExplorerView"
class="eclipseTradeClient.ExplorerView"
id="EclipseTradeClient.explorerView">
此後,進行保存(見圖7)。
圖7.進一步重構-手工更新Plugin.XML
3. 對於這種簡單的重構,情況就是這樣,對嗎?是的;但遺憾的是,你還沒有結束。打開類ExplorerView,改變靜態變量ID-把它初始化為EclipseTradeClient.explorerView。這相應於你剛才在plugin.xml中設置的ID。
4. 最後,你完成重構。現在,讓我們測試一下是否一切改動正常。切換回編輯器中的plugin.xml,並且點擊"Overview"選項卡。點擊"Launch an Eclipse application",這應該導致如圖8所示結果。
圖8.啟動Eclipse Trade Client程序
5. 現在讓我們改變結點的名字。打開類ExplorerView。找到內部類ViewContentProvider,並且改變方法"Object getElements(Object parent)",讓其返回一個字符串數組({"Watch List","Order History"})。
三. 把Spring Remoting添加到你的應用程序
下面,我們把Spring添加到你的Eclipse豐富的客戶端以便它向前一篇文章中的StockTradeServer工程發出請求。
首先,當開發Eclipse插件/RCP應用程序時,添加第三方庫的推薦的方法是通過另外一個插件。這樣做以後,你就不需要把這些第三方jars添加到你創建的每個工程。而是,你僅建立你的插件/RCP工程和第三方庫工程之間的某種依賴性。首先,我們假定你熟悉Eclipse的classloader。要點是,你必須采取一些額外的步驟來確保你的類在插件依賴性之間能夠彼此找到對方:
1. 在Eclipse中創建一個新的插件工程。並命名這個新工程為SpringClient。然後,設置如下值:
ID = SpringClient
Class = springClient.SpringClientPlugin
在填充Plug-in屬性後,點擊Finish。對於SpringClient插件工程,你不需要任何模板,因為其主要目的是存儲Spring庫和任何Spring相關的服務類。
2. 在你的解壓的spring-framework-1.2.8發行包中,你會找到下列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下
然後,把它們全部復制到你的SpringClient根目錄下。
3. 在Eclipse的包資源管理器中,右擊SpringClient以打開工程屬性。選擇"Java Build Path",點擊"Libraries"選項卡,並且把剛才你通過按鈕"Add JARs"添加的所有的那些jar文件加入。請確保你也導入了這些庫!點擊"Order and Export"選項卡,並且檢查所有的庫(見圖9)。通過這樣做,你就可以確保,當你創建對SpringClient的一種工作依賴性時,spring和log jars也是可用的。此後,點擊OK。
圖9.輸出第三方庫
4. 現在,你要修改SpringClient的manifest。在包資源管理器中,展開SpringClient->META-INF並且打開MANIFEST.MF。點擊"Runtime"選項卡並且點擊Classpath部分的"Add"。全選Spring jars和logging jars並且點擊OK。現在,在"Exported Packages"節中,點擊Add。選擇所有的包以便導出,並點擊OK(見圖10)。
圖10.把第三方庫添加到插件Classpath並導出包
我以前提及過,Eclipse的classloader經常引起問題。為了補救這一點,你可以點擊MANIFEST.MF選項卡並且添加下面一行:
Eclipse-BuddyPolicy: registered
5. 現在,讓我們添加spring配置文件。在Package Explorer中,轉到src目錄,創建一個新文件applicationContext.xml,並且加入下列內容:
在src目錄下,另外創建一個新文件beanRefFactory.xml並且加入下列內容:
不必感到驚訝,這些配置文件與你對stocktradeserver工程進行單元測試時使用的spring配置文件是相同的,除了你重命名了applicationContext.xml以外。
6. 為了簡化問題,你可以把類從stocktradeserver工程復制到SpringClient的src目錄下。在SpringClient的src目錄下面,創建包stephenlum.services.stock和stephenlum.services.stock.dto。
如果你還沒有准備好,你可以下載本文源碼或參考我的前一篇文章並且復制stephenlum.services.stock下的類StockService.java。然後,復制在stephenlum.services.stock.dto下的類StockDTO.java(見圖11)。
圖11.完成上面操作後的Package Explorer看上去的樣子
7. 把代碼添加到類SpringClientPlugin以裝載Spring的應用程序上下文。注意,你要把該代碼添加到構造器中。下面是新的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;
/**
*應用於桌面的主插件類。
*/
public class SpringClientPlugin extends AbstractUIPlugin {
private BeanFactory beanFactory;
//共享實例.
private static SpringClientPlugin plugin;
/**
*構造器.
*/
public SpringClientPlugin() {
plugin = this;
BeanFactoryLocator beanFactoryLocator = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference beanFactoryReference = beanFactoryLocator.useBeanFactory("ctx");
beanFactory = beanFactoryReference.getFactory();
}
/**
*在插件激活時調用這個方法
*/
public void start(BundleContext context) throws Exception {
super.start(context);
}
/**
*當停止插件時,調用這個方法
*/
public void stop(BundleContext context) throws Exception {
super.stop(context);
plugin = null;
}
/**
*返回共享實例.
*/
public static SpringClientPlugin getDefault() {
return plugin;
}
/**
*返回在給定的插件相對路徑下的圖像文件的一個圖像描述符
* @param path-路徑
* @返回圖像描述符
*/
public static ImageDescriptor getImageDescriptor(String path) {
return AbstractUIPlugin.imageDescriptorFromPlugin("SpringClient", path);
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
}
8. 最後,添加依賴性以實現工程EclipseTradeClient依賴於你的新插件工程SpringClient。在工程EclipseTradeClient中,打開plugin.xml並且點擊"Dependencies"選項卡。在"Required Plug-ins"節中,點擊Add,選擇"SpringClient(1.0.0)",並且點擊OK(見圖12)。
圖12.把SpringClient添加為一個要求的插件
現在,你必須在EclipseTradeClient manifest文件中添加與Eclipse的伙伴策略相關的內容。在文件plugin.xml中,點擊MANIFEST.MF選項卡並且添加下列入口:
Eclipse-RegisterBuddy: SpringClient
四. 創建一個新的WatchListView
現在,你可以開始創建你自己的視圖類了。首先,你要創建一個WatchListView,它將向應用程序服務器的StockDataService發出一個請求:
1. 在plugin.xml中,轉到extensions選項卡。
2. 在All Extensions樹中選擇org.eclipse.ui.views,然後點擊Add。
3. 隨後出現一個新的對話框窗口。在Extension Points樹中滾動並且選擇org.eclipse.ui.views。在相應於org.eclipse.ui.views的可用模板中,選擇SampleView,然後點擊Next(見圖13)。
圖13.新的擴展對話框
4. 在"Main View Settings"窗口中,填寫如下內容:
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"保持選擇狀態並且點選"Add the view to the resource perspective checked"(見圖14)。點擊Next。
圖14.針對於Watch List視圖設置"Main View Settings"
5. 在"View Features"下,保持默認設置並且點擊Finish。
6. 現在,你會在plugin.xml的"All Extensions"選項卡中看到新的"View and Category"。
7. 現在,你可以開始編寫你的Watch List視圖了。這個觀察列表是一個表格,因此首先要為此表實現接口ITableLabelProvider。在包eclipseTradeClient.views.watchlist下創建一個新類WatchListTableLabelProvider。你可以把一個ITableLabelProvider當作是JFace的等價物-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將出現於該頁面的底部:
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. 現在,你可以在類WatchListView中進行添加。我盡量保持模板生成的代碼不動以便於你可以自由地添加你的代碼。實質上,你是在添加一個表格-它將顯示包含在一個類StockDTO實例中的所有信息。因此,表格中的列也是基於StockDTO的成員。我已經重命名了兩個生成的Action-現在action1能夠從stocktradeserver中取回股票的列表並且在表格中顯示它們,而action2從表格中刪除所有元素(請參考源碼中的列表1.eclipseTradeClient.views.watchlist)。
五. 運行應用程序
現在,你可以運行你的應用程序了。如果還沒有准備好的話,你可以把stocktradeserver工程按如下步驟導入到Eclipse:
1. 在Eclipse中,點擊工具欄按鈕"Deploy MyEclipse J2EE project to Server"(見圖15)。
圖15.發布MyEclipse J2EE服務器按鈕
確保在列表下的工程是stocktradeserver。點擊Add,選擇Tomcat 5作為你的服務器,並且點擊Finish。當你看到一條消息"Successfully deployed"時,點擊OK(見圖16)。
圖16.StockTradeServer被成功發布
現在,啟動Tomcat服務器(見圖17)。Tomcat應該會成功地啟動。
圖17.通過MyEclipse插件啟動Tomcat
2. 啟動"Eclipse Rich Client"。打開EclipseTradeClient's plugin.xml文件,點擊"Overview"選項卡,並且點擊"Launch an Eclipse application"。當應用程序啟動時,按下圖18中的紅色按鈕以得到一個股票列表。這一行為將使用Spring HttpInvoker從應用程序服務器取回股票列表。你可以按下紅色圓圈右邊的按鈕來清除股票列表(見圖18)。
圖18.EclipseTradeClient成功運行
一切順利!你已經成功構建了一個小型的Eclipse豐富的客戶端程序並且使用Spring remoting技術把它連接到一個應用程序服務器上。
六. 小結
總之,基於Eclipse RCP構建你的胖客戶端程序將會大大減少構建這種程序的GUI框架所需的繁重代碼。另外,通過把Spring remoting用作客戶端/服務器通訊機制還允許你輕松地實現協議的切換,同時還提供其它所有在服務器端的Spring優點。