Welcome

首页 / 软件开发 / Flex / 使用Grails和Flex开发JEE应用

使用Grails和Flex开发JEE应用2010-04-20 infoq Maarten Winkels 译者:沙晓Java平台已经逐渐发展为一个成熟可靠的企业应用平台,成熟的应用平台的一个标志则是它能够带动大量的衍生技术以及可以与其他技术集成的选项。本文将详细讲述怎样用Grails这项传统JEE应用开发的衍生技术,结合另一项完全不同但却可以在Java中使用的Flex技术来开发JEE。这两个平台都能大幅度提高开发效率。两者相结合则在为J2EE应用创建富客户端的同时不影响整体的开发效率。

Grails的前身是一个在JVM中运行的web应用,它使用Groovy以及其它几个著名的框架,比如Spring和Hibernate。为了实现快速应用开发,它极为依赖“Convention over Configuration”原则。Groovy拥有很多动态的特性,在定义组件间共同行为方面,功能非常强大。Grails采用plug-in构架,因此很容易把它与其他框架集成,而且也很容易在应用间复用各自的功能。

Flex是个RIA开发套件,由它创建的SWF应用只能在FlashPlayer下应用。这是Adobe(前身为MacroMedia)的一个新型 Flash开发套件。除了拥有丰富的widget和把各种widget粘合在一起的强大的语言之外,它还能提供一些高端通信解决方案,分布式应用程序的开发因此变得相当容易。它使用两种语法:MXML和ActionScript。MXML创建在XML语法之上,专门用来定义通用组件的用户接口;而 ActionScript则用来定义组件之间的动态交互。

Grails和Flex的集成——难题所在

要把Grails和Flex这两个建立在完全不同基础上的框架结合起来,首先会遇到诸多通信方面的问题:

一个框架中的组件如何才能在另一个框架中找到正确的通信对象?

从本质上来说,Grails实际是运行在服务器的JVM上的一个web应用框架。Flex则是拥有客户端和(瘦)服务器组件的RIA平台,服务器组件以web应用的方式部署。因此,这两个框架之间的集成实际上在web应用容器内进行。

用户在Flex UI发起的通信必须通过Grails组件来调用业务逻辑。那么,Flex UI组件该如何找到正确的Grails组件呢?

框架间如何解析彼此的数据?

Flex采用ActionScript来描述数据,而Grails则采用Java和Groovy对象。Flex UI向服务器发送的ActionScript对象应该被转述为应用程序能够理解的数据结构。这该如何实现?

某个用户的修改该如何与该应用程序的其他用户交互?

这是多用户应用程序普遍存在的问题,但同时运用两个不同的框架使得问题更加复杂。难点在于Grails应用程序,用户通过Flex UI来启动这个应用,但如何通过Flex UI与其他用户通信,让他们知道该用户的这一动作呢?

在接下来的三个部分中,我们详细讨论上文提到的三个问题,寻找采用Grails和Flex的解决方案。

集成——寻找消息接收对象

一个框架中的组件如何才能在另一个框架中找到正确的通信对象呢?

具体到Grails和Flex的话,这个问题其实就是在问Flex组件怎样才能找到正确的Grails组件,进而发送请求数据,或者以用户的名义执行一些操作。为了更好的理解解决这个难点的方法,我们首先来了解一下Flex的通信子系统。

Flex中的客户——服务器通信

Flex的通信子系统可以分为客户和服务器两个部分。客户部分包含了那些允许应用程序发送或者接受消息的组件,比如RemoteObject和 Consumer组件。这些组件与服务器部分特定的“服务”对象相关联,比如RemotingService和MessagingService。客户组件及其相关联的服务器组件的结合能够支持典型的通信模式。比方说结合Consumer、Producers和MessagingService,应用软件就能够使用Publish-Subscribe机制来通信。

客户和服务器件的通信通过信道(Channel)来完成。信道的实现方式并不唯一,所有信道中最重要的是AMFChannel和RTMPChannel。AMFChannel建立在HTTP基础上,也就是说建立在请求-回复的构架上。它可以和MessagingService同时使用,从而支持 Publish-Subscribe构架。这种结合下,信道定期从发布中读取新的消息,生成请求。RTMPChannel在这样的配置下效率更高,它能够在TCP/IP的基础上支持客户与服务器间的连接。这样一来,客户与服务器之间能够立即发送或接受消息。遗憾的是,Adobe免费开源的Flex实现—— BlazeDS不包含这样的RTMPChannel实现。

Flex中最重要的通信基础设施是Destinations。Destination是通信信道的服务器端终点。一个服务提供一个 Destination,而客户组件则通过这个Destination与这个服务相关联。关联的客户组件可以向Destination发送和读取消息。Destinations可以由Factories创建。

Grails暴露的远程接口:服务

如何把Flex复杂的通信设施和Grails结合起来呢?Grails能够识别几类对象:域对象、控制器、视图和服务。Grails中的每个服务都是通过外部通信信道——比如HTTP——展示某些功能或者服务的一个对象。而在Flex中,每个服务则与一个Destination相对应。

这恰恰就是针对Grails的flex-plugin所提供的解决方案。Grails中所有注明向Flex展示的服务都将在Flex框架中以Destination的形式注册。Grails通过一个特定的 Factory把注明的服务添加到Flex中特别配置的RemotingService。这个特定的Factory会在Grails使用的Spring上下文中定位到对应的服务。所有这些配置都可以在services-config.xml中找到,flex-plugin会为Grails将这个文件复制到正确的地方。

class UserService {
static expose = ["flex-remoting"]
def List all() {
User.createCriteria().listDistinct {}
}
def Object get(id) {
User.get(id);
}
def List update(User entity) throws BindException {
entity.merge();
if (entity.errors.hasErrors()) {
throw new BindException(entity.errors);
}
all();
}
def List remove(User entity) {
entity.delete();
all();
}
}

这段配置将UserService展示给flex客户。下面这段MXML代码则是对前面这段配置的应用。RemoteObject的 destination是userService,这个userService正是Grails中目标对象提供的服务名。服务对象的所有方法这下都可以作为远程操作调用。ActionScript可以将这些操作像一般的方法那样调用,而方法调用的结果或错误也可以当作一般的ActionScript事件来处理。

...
<mx:RemoteObject id="service" destination="userService">
<mx:operation name="all" result="setList(event.message.body)"/>
<mx:operation name="get" result="setSelected(event.message.body)"/>
<mx:operation name="update"/>
<mx:operation name="remove"/>
</mx:RemoteObject>
...
结论

flex-plugin为Grails提供的针对集成的解决方案非常漂亮,易于使用而且几乎是自动化的。在Convention-over-Configuration概念下,Destinations动态添加到Flex配置的时候使用命名规范。