摘要 eclipse丰富的客户端平台(rcp)正在快速地成为构建胖客户端应用程序的框架选择。本文将向你详细介绍如何利用eclipse rcp进行spring 。
一. 引言
尽管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优点。