原文地址 http://www.onjava.com/pub/a/onjava/2004/12/01/flexjava.html
By MarkEagle
12/01/2004
今天我们开发的 J2EE 网络应用程序,在表现层常用的就是Struts, Tapestry, WebWork, or Spring。这些工具一般使用MVC体系结构,输出HTML到 浏览器。典型的程序网络开发模型就是要求 用户对程序的每一个动作都要发送请求到服务器上。对于程序的每个用户请求,服务器生成一个回复允许用户提交一个新请求用以获得更多信息。一个浏览器通常用 来为用户渲染界面。但是浏览器是一个有太多限制的客户端,同时缺乏开发和用户体验。
富网络应用程序(RIA)技术用来处理表现层的缺陷。这篇文章将以注重实践的态度来理解什么是RIA,怎样把它融入你的应用体系中。文章也将提出一种是跟几个流行的 开源框架组合潜在的挑战。
浏览器的局限
当前已有的解决方案的问题是什么呢?当程序行为正确时也许并没有什么问题,可是曾经大多数的网络开发者都抱怨在使用浏览器作为客户端的能力限制。这里有几个当网络应用程序使用浏览器产生的问题:
l 各种浏览器以一些不协调的方式解释象javascript这样的脚本语言,这迫使开发者花费出几倍的时间写相同的代码来适应不同的浏览器。
l 一些象标签,向导表单,大型数据列表处理 等普通的功能却困扰着开发人员并需要付出额外的实践来为浏览器写代码。
l HTML本身就有局限性,静态的标签无法扩展。
l 用户界面上的事件处理有着巨大的挑战,因为渲染HTML页面只能同时显示一个,事件无法不通过服务器更新其它页面上的数据。
l 存储程序状态只能通过无法是用对象的cookies,
l 使用浏览器几乎无法开发需要脱机工作的程序
这些例子反复说明了大部分开发者都已知道的:当前的工具是由局限性的。开发者使用浏览器时经常会因为这些问题要去找到解决方案。开发者和用户都已经对这种瘦客户端的能力失去信心。
富网络应用程序
有一种克服以上一些局限性的办法,我们称之为RIA,一个RIA 提供用户一个扩展了浏览器无法实现的能力的胖客户端。大多数普通的J2EE RIA客户端是java和flash。当需要开发一个以数据为中心的大型应用程序时,RIA通常比较适合。一些可用的开发RIA解决方案有 JDNC (JDesktop Network Components), Laszlo, Thinlet, Java Web Start, and Macromedia Flex。
RIA能解决上面所说的问题,以下是一些RIA能提供的功能列表:
l RIA 提供类似于浏览器的 UI组件,增加了新本地化,更多组件。例如:包含数字递进器,滑动控制,在线数据表格逐渐,和 菜单栏。
l RIA允许使用布局管理组件,象标签向导,折叠栏,树型,还有一些其它的布局,已经接近AWT和SWING开发。
l RIA提供拖动-释放 能力
l RIA中的语言风格对所有客户端都是一致的,所以无需为不同应用重写
l 请求/回复 模型不是所有用户界面动作说必须的。使用RIA,用户与界面交互只需要处理所需要的部分。RIA 可以使用HTTP协议方法上传数据到应用程序服务器。通常,无论如何,首选的机制是remoting,它支持不同的方式使用RIA。RIA 特别的使用一些扩展协议来在HTTP之上交流。
l 多组件事件处理可使用。
l RIA允许你存储更多信息在客户端代替httpsession,这减少了服务器的内存消耗。
l 持久化状态,大多是在form对象,提供脱机时的能力
RIA是一个比较新的技术,引进了新的观念来发展这些类型的应用。它不是所有应用程序的银弹,根据实现,提供试验。但是,假如你的应用能够受益于富UI设计,那么RIA也许适合你。这篇文章现在将介绍一个RIA解决方案- Macromedia Flex,我们还将讨论整合问题。
Macromedia Flex
Macromedia Flex 是 一种提供RIA的商业表现层服务器。Flash插件是flex程序运行时环境所不可缺少的。大多数浏览器已经预装了flash插件,这将有助于你 立刻使用flex。我们将讨论是用flash插件代替java插件与J2EE应用服务器交互的一些问题。
开发者使用两种核心的语言来创建flex程序,第一个是MXML,Macromedia Flex标记语言,它包含了大量的XML标签,允许开发者布局他们的界面。 MXML能引用到XUL,或者XML UI语言,这些标记可以被扩展,有程序需要的额外能力,不象HTML。其它MXML结构允许你拥有自己的 look and feel MXML组件。
第二个语言是 ActionScript 2.0,它是一种ECMA-compliant 语言很像 JavaScript。ActionScript元素编码在MXML页面中。这是一种强类型的面向对象的语言,很容易被熟悉 java的开发上手。ActionScript 还有丰富的事件处理能力允许应用程序响应动态的用户交互。因为ActionScript 在flash插件中运行,所以无需为不同的浏览器编写相似的代码。
MXML跟ActionScript 都是基于文本的语言,能够在普通的文本编辑器,一个象eclipse 的IDE,或者其它成熟的工具 象flex builder。假如你有java,XML经验,或一些脚本语言象 javascript,你就只有很小的学习曲线学习flex开发。
Flex 服务器 是为了将MXML和ActionScript 编译转化成flash 字节码.swf文件。这个过程就像java web服务器容器编译JSP文件成servlet。Swf文件在客户端的flash运行时环境中运行。Flex 服务器提供其它类似缓存,并发,处理远程对象的功能。
为你的遗留体系引入一个RIA框架
现在你已经有一些RIA方面的理解了,让我们来看看怎样为你已经存在的应用程序引入一个RIA框架。这将包括一些关于RIA应该在少耦合的程序中处于什么层的外置的讨论。此外,讨论将会在特别强调一些可能存在在开发flex与一些流行的开源框架结合的缺陷。这个例子会帮助你确定在引入RIA时潜在的问题。
让我们开始标识一个分层的体系结构。一个这样的体系一般包括 表现层,业务委托层,业务综合/服务层,和持久层。这是一个可能的个层实现关系:
Flex + Business Delegates + Spring Framework + Hibernate
接下来的讨论将集中在 综合各个层。
关于我这个已经存在的MVC表现层
表现层是一个web应用程序展现给用户的界面,把请求转发到服务器,存储数据信息模型。刚开始的RIA开发者总是希望能重用已经存在的struts组件。但是象flex这样的产品提供了他自己的MVC架构。你会真的需要去维持并存两个表现层框架在你的应用中吗?
让我们看看一个实际的例子中flex客户端发出一个通过struts组件的请求到应用程序服务器上。一个来自flex的请求发送到struts表现框架之后被高层的程序接收。图1显示了一个这样的例子
图1 :怎么样用其它java组件组合 flex 和 struts
象struts这样的表现成框架在HTTP上传送HTML请求,虽然我们可以使用HTTP协议,但更鼓励开发者使用 remote object 调用,因此,使用这两个表现层框架会逐渐产生协议不搭配,除非我们有一个特殊的需求直接用RIA组合struts。,避免他们。图2 显示一个更好的组合flex 和struts的方案:
图2 ,介绍flex struts 平行存在于 java 组件中
图2 提出了怎样去隔离 struts 组件和flex 组件使他们共存。这满足了应用程序需要平行的RIA组建和 轻量级 类struts组件。
开发者必须明白他们使用RIA客户端是要做什么。这要求我们明确的转换我们熟悉的传统的请求/应答这种编程思想。象flex这样的RIA产品不像struts这样是 请求或者应答 驱动的。RIA 客户端更新UI而无需全部的实例都返回服务器进行交互。
Struts 并不是你在使用RIA时需要唯一考虑的问题。我们只需花费一点时间就可以熟悉这些类型的技术。在这个学习路途中,最大的问题是组合java服务器端的组件。底线是不背离RIA观念。
在业务层集成flex
现在我们了解了一些表现层技术,让我们来讨论一下对程序结构的其它层的影响。我们要重新配置我们的表现成组件,怎样跟业务层组合在一起呢?
Flex 是一个可扩展的RIA框架,提供了很多方法来与你的J2EE组件进行通信。Flex提供 HTTP 通信,web service通信,和 macromedia公司所特有的AMF(ActionScript Messaging Format)网关。AMF网关是一个高性能的二进制协议,就像flash里面的 remoting协议。Remote对象使用HTTP协议通过AMF网关传递信息。Flex为所有这些通信协议提供MXML标记,极大地减少了编码复杂度。此外,flex允许你使用同步的或者异步的方式去调用远程的业务层。当使用异步远程调用时,用户可以执行一些客户端行为,而不会有传统web应用程序的阻塞现象。当然,你可以在适当的地方使用同步方法来阻塞用户操作。
现在让我们来考虑 怎样在业务/综合层中集成flex。讨论中我们将使用spring框架作为我们的综合层,当然这并不限制你使用任何你自己选择的其它实现方式。让我们假设你有一些服务操作在你的spring微容器中,而你需要在flex中调用这些远程对象。
既然flex 无法直接知道任何关于spring的东西,所以你用一个担当代理服务器组件的小层来分离他们。 另外,既然spring在java接口中有不俗的表现,它也一定适合去构建一些实现了一些spring服务接口的代理对象。这些代理对象将会提供一个在flex跟综合层之间的分隔网关。唯一你需要去做的就是在flex配置文件中配置这些对象,然后你就可以通过AMF网关调用了。这里有一个关于在服务器端 flex-config.xml文件中怎样配制代理对象的例子:
<object name="OrderBusinessDelegate">
<source>
com.meagle.flexro.FlexBusinessDelegate
</source>
<type>stateless-class</type>
<use-custom-authentication>
true
</use-custom-authentication>
<allow-unnamed-access>
false
</allow-unnamed-access>
<roles>
<role>OrderUser</role>
<role>Admin</role>
</roles>
</object>
第一眼你就可以看出来flex一些特别的能力,如配置安全选项,和确定代理对象是有状态的还是无状态的。当一个远程对象调用一个综合层对象时,调用会被flex代理java对象截获。代理将会产生一个对综合层或服务层的调用。结果对象将会被通过AMF网关发送回flex客户端,在那里被解释成actionscript对象。这里有一个MXML代码使用远程调用和存储结果在数据模型中的例子:
<mx:RemoteObject id="soapro"
named="OrderBusinessDelegate"
protocol="https"
showBusyCursor="true">
<mx:method name="saveNewOrder"
result="saveNewOrder_result(event)"
fault="faultHandler(event.fault)"/>
<mx:method name="findOrderById"
result="findOrderById_result(event)"
fault="faultHandler(event.fault)"/>
<mx:method name="updateOrder"
result="updateOrder_result(event)"
fault="faultHandler(event.fault)"/>
</mx:RemoteObject>
<mx:Model id="roModel" >
<!-- The object graph for the Order object
will be stored here -->
<Order/>
</mx:Model>
域对象是用就类似于actionscritp 的java写成,它来回于AMF网关。这个过程开始于一个来自于flex客户端的请求,通过AMF网关到应用程序的其它层。结果对象图表将会通过其他java层被发送回,最终通过AMF网关回到客户端。当一个对象通过网关他会被转换回actionscript等价物。图3 显示了这个过程
图3 AMF 网关的 轮廓图
其它的一些关于返回对象 通过flex和你的java层: 因为actionscript 2.0是一个面向对象的语言,它可以创建actionscript的java等价物 对象。这样就使得在对象来回穿越于AMF网关变得容易多了。Actionscript 对象被发送回flash插件类似于 数据转换对象DTO,这是必须的,因为flash插件没有任何java运行时组件。这里有一个熟悉的例子,一个java的命令 域对象:
package com.meagle.bo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This object represents an order.
* @hibernate.class table="TestOrder"
* @author meagle
*/
public class Order {
private int id;
private double total;
private int version;
private String userName;
private List orderLineItems = new ArrayList();
// other fields and getter/setter methods not
// shown to save space
}
下面是一个 actionscript 等价物:
/**
* Generated Action Script Object
* for com.meagle.bo.Order. Do not edit!
*/
class com.meagle.bo.Order extends Object {
public function Order(){}
public static var regClass =
Object.registerClass("com.meagle.bo.Order",
com.meagle.bo.Order);
var id : Number;
var orderLineItems : Array = new Array();
var total : Number;
var userName : String;
var version : Number;
// other fields and getter/setter methods not
//shown to save space
}
你已经注意到了在actionscript Order对象中有一个特殊的方法Object.registerClass()。这个Object.registerClass()方法是用来使AMF网关知道如何去耦合和解耦 java和actionscript 对象。这个方法注册了 对应于 服务器端 java类的 客户端actionscript 类。因为这些对象如此的相似,你无需去重写你的域对象,只需要一些细微的格式化。一些象Xdoclet 和ant 这样的工具允许你自动生成 这些 actionscript 对象。现在你可以在flex 客户端操纵你的java对象就像 actionscript一样。
在持久成集成你的flex
在一个结构良好的的网络应用程序中,我们一般不直接跟持久层通信。使用flex不会改变这个架构。大多数情况下综合层会与你的持久层通讯。一般我们使用DAO 来访问在一个象数据库一样的存储地的数据。Flex 客户端也不会直接与持久层通信,即使知道这些层的存在,因为这样会产生强耦合。让我们使用hibernate 来做持久成做一个范例吧。
使用hibernate和Macromedia的 AMF网关的远程对象有两个缺点。Hibernate用户知道你不能访问一个懒加载的集合collection,因为你还没有在 session中初始化他们。访问一个没有被初始化的动态代理对象集合会导致运行时异常。AMF网关不知道如何去 寻找hibernate动态代理对象。一种可能的解决方案是面向方面的编程(AOP)拦截器,它能放置一个将要通过AMF网关的对象在一个委托对象中,然后移除动态代理。这个过程包括发送结果对象穿过一个递归查询那些使用反射和没有被初始化的代理对象的拦截器类。假如一些懒代理对象或者集合被发现了,他就会把他们的值设置为null。这是一个面向方面的一个截面,我们可以使用一些象 JBoss AOP, AspectJ, Spring AOP等等 AOP语言来实现。AOP 拦截器将在业务代理层中应用。图4 显示了这样的程序架构的框架:
图4 在通过AMF网关之前引入AOP拦截器和advice来代理对象. 跟进一步说,它会在一些像综合层,持久层等地方减少耦合。
好消息是AMF 网关不知道如何你缓存双向对象,所以无止境的递归操作也不会在转换对象过程中产生。因此你可以在传送它们往返于AMF网关时保持这些完整的关系。 还有,因为对象是无连接和拷贝过来的,你必须使用Session.saveOrUpdateCopy(Object object) 方法持久化你的结构到数据库当中。这个方法必须被使用,因为对象在穿越AMF网关的过程中丢失了一些hibernate可以使用的特殊的字节码信息。
认证
典型的 J2EE 应用程序有一些相同的认证设计。也许是 基于容器的认证模式或者是自定义的用户认证代码。像flex这样的RIA服务器允许你在flash客户端使用自定义认证表单,或者 在大多数应用程序服务器上基于容器的认证。此外,假如你要再上面的这个例子中查找业务委托配置,你就会注意到你能够安全的为这些对象配置角色。经常做的是在AMF网关中允许开发者获取HttpRequest,HttpResponse和ServletConfig对象再你的代理对象方法中使用来增强安全性。
总结
这篇文章有目的的介绍了一些在使用象flex这样的RIA时 进行的权衡和可能的缺陷。不管你是否使用flex或者其他RIA实现,这里有些在使用这些技术主要的考虑的事情。当评价一个RIA框架,确定它在遇到一些问题时是否有足够的可扩展性。另外,在RIA和java之间传送数据需要注意小心评价综合的问题。
资源
Mark Eagle is a Senior Software Engineer at MATRIX Resources, Inc. in Atlanta, GA.