本章简介

第3章讲解了视图状态、Flex页面间的跳转、Flex应用的模态窗体、数据绑定、使用拖放,图表等知识。本章将学习Flex与外部的数据通信。在实际开发过程中,Flex应用的数据往往来自于业务逻辑数据提供的服务器端。虽然Flex是用于开发富客户端界面的强大平台,可以编写业务逻辑,但从架构的角度看,仍然需要将核心的业务逻辑放在Flex程序之外。Flex与外部程序的数据通信主要包括HTTPService. WebService和Remoting 3种方式。

核心技能部分



从分层角度来看,企业应用系统主要分为三个层次,如表4-1-1所示。

表5-1-1企业应用主要分层


企业应用层次




职责简介



展现层

主要负责信息展示和人机交互的相关逻辑

领域层

主要完成企业应用业务逻辑,是系统的核心部分

数据源层

负责数据的提取和持久化

展现层主要负责信息展示以及用户与软件之间的交互逻辑,“展现层”接受用户输入并将用户的意图转换为对“领域层’或“数据源层’逻辑的调用。

领域层也被称为“业务逻辑层”,主要解决应用所针对业务领域的问题。该层负责校验来自“展现层”的输人数据,根据“展现层’用户指令进行业务逻辑处理,调用“数据源层’的逻辑实现数据的访问和持久化。

数据源层主要负责数据访问和持久化,数据可能来自于数据库或者消息系统。对干绝大多数企业应用来说,数据源层主要负责同数据库系统的交互。

Flex+Java企业应用中,“展现层’逻辑完全运行在客户端的Flash虚拟机中,而“领域层”和“数据源层”逻辑则运行在服务器端的Java虚拟机中,如图5.1.1所示。


图5.1.1 Flex+java企业应用层次逻辑分布图


 从图5.1.1中可以看出,客户端系统与服务端系统完全用不同的语言实现,因此系统是异构的。同时,客户端代码运行在客户端的ActionScript虚拟机中。而服务器端代码则运行在服务器上的Java虚拟机中,因此系统又是分布式的。这与我们开发传统Web应用完全不同,

传统Web应用中所有Java代码,包括业务逻辑代码和生成人机界面的代码都在服务器Java虚拟机中执行,如图5.1.2所示。


图5.1.2 传统web应用层次逻辑分布图

    基于传统Web技术进行开发,很多开发者已经习惯了“接受客户端的请求,然后执行业务逻轼,最后输出人机界面”这种工作模式。基于Jsp技术的MVC框架,比如Struts,Jsf等,都是基于这种工作模式开发的。

    因此、Flex+Java所开发的BS应用与传统Web所开发的B/S系统最大的区就是:使用Flex+Java开发的B/S应用系统中,B系统(客户端系统)和S系统(服务器端系统)完全分离、各自独立地运行在不同的CPU和虚拟机中。B系统主要负责“展现层”逻辑,而S系统主要负责“领域层”和“数据源层”逻辑。因此,Flex+J ava所开发的企业应用系统是异构的分布式系统,这种异构分布式系统给我们带来了以下需要思考的问题:

Ø 异构的客户端系统和服务器端系统如何通信?

Ø 如何保持分布式的客户端系统和服务器端系统之间的状态一致性?

    我们在进行架构设计时,必须要清楚并解决这些问题,才能顺利进行企业应用开发,下面两个小节主要针对以上两个问题进行阐述,并给出解决方案。

1.1 确定Flex客户端系统和Java服务器端通信框架

    开发异构系统时,如何进行通信和传递数据是我们比较关注的问题。使用Flex+Java开发基于B/S结构企业应用,客户端和服务器端的通信协议是我们所熟知的HTTP协议。在Flex中,基于HTTP协议访问服务器的通信组件有三个:

Ø    HttpService(mx.rpc.http.mxml.HTTPService)

Ø    WebService(mx.rpc.soap.mxml.WebService)

Ø    RemoteObject(mx.rpc.remoting.mxml.RemoteObject)

HttpService组件可以调用很多不同技术的服务端页面,比如JSP,ASP ,PHP,Servlet等在大多数情况下,使用HttpService访问服务器端页面来完成Flex客户端与服务器端的数据交互,服务器端返回的结果一般都是XML格式的数据。下面是Adobe官方关于HTTPService组件的例子应用:

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"

   creationComplete="feedRequest.send()" layout="absolute">

   <mx:HTTPService id="feedRequest"

      url="http://weblogs.macromedia.com/mchotin/index.xml"

      useProxy="false" />

   <mx:Panel x="10" y="10" width="475" height="400"

        title="{feedRequest.lastResult.rss.channel.title}">

       <mx:DataGrid id="dgPosts" x="20" y="20" width="400"

           dataProvider="{feedRequest.lastResult.rss.channel.item}">

           <mx:columns>

              <mx:DataGridColumn headerText="Posts" dataField="title"/>

              <mx:DataGridColumn headerText="Date"

                   dataField="pubDate" width="150" />

           </mx:columns>

       </mx:DataGrid>

       <mx:LinkButton x="20" y="225" label="Read Full Post"

                   click="navigateToURL(new URLRequest(dgPosts.selectedItem.link));"/>

       <mx:TextArea x="20" y="175" width="400"/>

   </mx:Panel>

</mx:Application>

这个例子的运行需要能够访问互联网,在本例中通过调用URL为http://weblogs.macromedia.com/mchotin/index.xml的HTTPService ,返回了一个XML文件,并将这个XML作为Datagrid控件的dataProvider,从而通过Datagrid将XML文件中的数据展示出来。XML中的数据主要是网站最近的发帖记录。这个例子说明,HTTPService的工作方式主要通过请求URL获取XML格式数据。

上例运行后 效果如图5.1.3所示。


图5.1.3 HttpService 示例

    同HTTPService类似,Flex应用可以调用URL所表示WSDL 1.1服务,返回SOAP1.1格式的调用结果。SOAP也是基于XML格式规范,因此。使用HTTPService和WebService组件同服务器之间的交互都是通过XML进行的。下面是Adobe官方关于WebService组件的例子应用。  

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"

layout="absolute"

creationComplete="wsBlogAggr.getMostPopularPosts.send()">

<mx:WebService id="wsBlogAggr"

     wsdl="http://weblogs.macromedia.com/mxna/webservices/

mxna2.cfc?wsdl"

     useProxy="false">

      <mx:operation name="getMostPopularPosts">

         <mx:request>

            <daysBack>30</daysBack>

             <limit>{cbxNumPosts.value}</limit>

         </mx:request>

     </mx:operation>

</mx:WebService>

<mx:Panel x="10" y="10" width="475" height="400" layout="absolute"

    title="Most Popular Posts">

    <mx:ComboBox x="30" y="25" id="cbxNumPosts"

       change="wsBlogAggr.getMostPopularPosts.send()">

       <mx:Object label="Top 5" data="5" />

       <mx:Object label="Top 10" data="10" />

       <mx:Object label="Top 15" data="15" />

    </mx:ComboBox>

    <mx:DataGrid x="30" y="75" id="dgTopPosts" width="400"

      dataProvider="{wsBlogAggr.getMostPopularPosts.lastResult}">

<mx:columns>

<mx:DataGridColumn headerText="Top Posts"

dataField="postTitle"/>

<mx:DataGridColumn headerText="Clicks" dataField="clicks"

width="75"/>

</mx:columns>

</mx:DataGrid>

<mx:LinkButton x="30" y="250"

label="Select an item and click here for full post"/>

</mx:Panel>

</mx:Application>


  在这个例子中,通过WebService组件调用了服务器所提供的WebService服务,返回SOAP

格式的XML数据,根据请求参数,XML数据表示网站中最近30天的点击率排名前5,10或者15的博客。

  通过这两个例子我们可以看到,使用HTTPService和WebService无需第三方框架,在服务器端直接编写相应的服务即可,所以比较容易理解和使用。但是,无论使用HTTPService还是WebService访问服务器,Flex客户端和服务器之间传递的都是XML数据,客户端和服务器端处理的也是XML数据。对于企业应用来说,客户端和服务器端交互的数据量往往很大,因此使用XML作为数据交换格式会降低传输效率和转换效率。同时,处理XML数据的代码也远比处理对象的代码繁琐,并且难以阅读和调试。因此,在企业应用开发中,客户端系统和服务器端系统之间采用HTTPService和WebService进行通信的部分较少,即使使用这两个组件。也应当用来传递少量、数据格式不易发生变化的数据。

    在企业应用开发中,Flex客户端与后台服务器之间的大量通信都是采用RemoteObject完成的。RemoteObject组件在“第三方软件”的配合下,能够调用后台服务器对象上的方法,比如Java对象或者.net对象上的方法,从而实现客户端与服务器端的通信。在客户端使用RemoteObject可以直接将ActionScript对象作为调用的参数和返回结果。这一点听起来似乎有些神奇,但其实也很容易理解:Adobe公司定义了一种二进制数据格式AMF(Action Message Format),用于客户端与服务器端的数据交互。    其实,使用AMF格式交换数据与使用XML进行数据交换的主要区别在于: AMF二进制数据的转换和传输效率更高,同时需要“第三方软件”用于解释AMF格式数据。Flex客户端RemoteObject组件与服务器端通过HTTP协议传递AMF格式的二进制数据进行通信的大致过程如下:

    1)客户端RemoteObject将调用参数中的ActionScript对象序列化为AMF数据格式,然后发出调用请求。

    2)服务器的“第三方软件“获取HTTP请求流。

    3)服务器的“第三方软件”对HTTP请求流进行解析,并且建立响应消息。对HTTP请求流进行解析,解析过程包括解释AMF格式数据,将ActionScript对象的AMF数据按照事先确定的协议“反序列化”为服务器端对象,比如Java对象,然后用这些参数调用客户端指定的服务器对象上的方法。

    4)服务器的“第三方软件,将调用的结果“序列化”为AMF格式的数据流。

    5)服务器发送HTTP响应给Fee客户端。

    6) Flex客户端解释AMF格式数据流,将调用结果序列化为ActionScript对象。

下面我们给出一段Flex官方文档代码来展示Rernoteobject对象的使用:

<?xml version="1.0"?>


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

    <mx:Script>

        <![CDATA[

            import mx.controls.Alert;

            import mx.rpc.remoting.RemoteObject;

            import mx.rpc.events.ResultEvent;

            import mx.rpc.events.FaultEvent;

            [Bindable]

            public var empList:Object;          

            public var employeeRO:RemoteObject;

            public function useRemoteObject(intArg:int, strArg:String):void {

                employeeRO = new RemoteObject();

                employeeRO.destination = "SalaryManager";

                employeeRO.getList.addEventListener("result",

                                                           getListResultHandler);

                employeeRO.addEventListener("fault", faultHandler);

                employeeRO.getList(deptComboBox.selectedItem.data);

            }

            public function getListResultHandler(event:ResultEvent):void {

                 // 远程调用成功所要完成的处理。

                empList=event.result;

            }


            public function faultHandler (event:FaultEvent):void {

             // 调用失败所要完成的处理。

                Alert.show(event.fault.faultString, 'Error');

            }

        ]]>

    </mx:Script>

    <mx:ComboBox id="deptComboBox"/>

</mx:Application>


在上面的代码中首先看一下函数useRemoteObject这个函数中首先使用语句:

  employeeRO = new RemoteObject();

创建了一个RemoteObject对象employeeRo。然后通过语句:

employeeRO.destination = "SalaryManager";

将RemoteObject对象的destination属性赋值为一个字符串" SalaryManager ",destination属性表示远程对象调用的“目的地’,请求发送到服务器端后,服务器端的“第三方软件’接收到请求后会检查配置文件,找到destination值所映射的“服务器端组件”,从而可以调用该组件上的方法。接下来使用语句:

employeeRO.getList.addEventListener("result", getListResultHandler);    

设置远程服务调用成功时的处理方法,使用语句:

employeeRO.addEventListener("fault", faultHandler);

设置远程服务调用失败时的处理方法。最后,使用语句:

employeeRO.getList(deptComboBox.selectedItem.data);

以deptComboBox.selectedItem.data为参数,调用destination属性所映射的“服务器端组件”的getList方法,这里,“服务端组件”必须有一个名为getList的公开方法,调用“服务器端组件”的getList方法是异步调用,因此它不会阻塞线程来等待调用结果的返回,调用结果的返回时会在getListResultHandler方法中进行处理。在getListlRsultHandler方法中,我们到语句:

empList=event.result;

    该语句表示远程调用所返回的结果event.result可以直接赋值给ActionScript对象。当然,

后端返回的对象类型与Flex客户端的ActionScript对象类型要满足“第三方软件’所规定的对象类型之间的“映射”规则,这样,Flex就可以把后台返回的AMF数据流自动地序列化为ActionScript对象。

    从上面的代码分析中我们可以看出:同传递XML方式相比,RemoteObject调用方式直接将ActionScript对象作为调用的参数和返回结果,这对于开发者编程特别方便。同时,通过RemoteObject调用远程方法需要多写几行代码,但通过精巧的封装可以很好地解决这个问题。

在上面的论述中多次提到了“第三方软件”,要使用RemoteObject组件进行远程方法调用,那么必须在服务器上部署和配置相应的“第三方软件”。“第三方软件”有两个最基本的作用:

Ø 服务器端对象序列化为AMF格式数据和将AMF格式数据反序列化为服务器端对象。

Ø 将客户端的请求映射为服务器端相应对象上的方法调用。

由于AMF规范已经公开,因此,有很多“第三方软件”支持不同的后台服务器端语言,在.net平台下比较著名的“第三方软件”为Midnight Coders WebORB。

我们所关心的Java平台“下的“第三方软件”有Adobe官方商业收费软件LifeCycle  Data  Service(LCDS)和Adobe官方开源软件BlazeDS。

BlazeDS是LCDS的开源版,只不过BlazeDS不具备LCDS的一些高级功能,比如:

Ø 高级客户端-服务器数据同步功能。

Ø 冲突检测/解决。

Ø Adobe AIR应用的离线数据管理服务。

Ø 由RIA生成PDF等。

使用BlazeDS与LCDS进行企业应用开发的配置完全一样,因此,在不需要LCDS高级功能的情况下,完全可以使用BlazeDS替换LCDS作为一种廉价方案,必须使用LCDS高级功能时用户可以追加投资购买LCDS,因此基于BlazeDS企业应用可以很容易升级为基于LCDS的企业应用,这也是我们选择BlazeDS作为配合RemoteObject远程调用的“第三方软件”的主要原因。

因此,使用Flex+Java开发企业应用,我们主要使用RemoteObject+BlazeDS实现Flex端与Java端的通信。

总之,使用RemoteObject+BlazeDS作为Flex端同Java服务器端的通信框架有如下优点:

Ø 以二进制的AMF协议传递数据,转换和传输数据的性能高于XML格式。

Ø 使用RemoteObject+BlazeDS能够实现Flex对象与Java对象之间的自动转换,更加有利于开发者编程。

Ø 使用开源框架BlazeDS所开发的企业应用可以更容易地升级为采用高端商业软件LCDS作为数据通信框架的企业应用。

当然,还有一些其他开源框架,比如Hessian。和Granite也能够完成与BlazeDS类似的功能,开发者可以根据实际情况加以选择,但是由于它们的原理相同,所以可以使用相同架构方法和设计模式。

1.2 构建一个简单的BlazeDS应用

    本节的任务是创建一个RemoteObject应用程序,这个应用程序包括两部分:前端Flex应用和后端Java应用。两者通过BlazeDS通信, 开发步骤如下。

1.准备软件环境

在Flex应用中使用Remoting技术时需要以下软件: 

Ø  MyEclipse 7.5及以上版本。 

Ø Adobe Flash Builder 4 Plug-in。 

Ø Tomcat 5.5及以上版本。 

Ø JDK l.6及以上版本。 

Ø BlazeDS 3.2及以上版本。 

2.安装配置软件

安装配置软件需要按照以下顺序: 

Ø 安装MyEclipse 7.5。 

Ø 安装Adobe Flash Builder 4 Plug-in.

MyEclipse7.5安装完成后将创建一个Common文件夹和启动程序所在文件夹(默认命名为“MyEclipse 7.5”)。启动程序所在文件夹中包含一个dropins文件夹,该文件夹是MyEclipse7.5中安装Adobe Flash Builder 4 Plug-in时所需的文件夹。选择“再插入一个Eclipse”项,单击“选择”按钮,选择MyEclipse启动程序所在的文件夹进行安装,如图5.1.4所示。 


图5.1.4 安装Adobe Flash Bulider 4 Plug-in

Ø 安装BlazeDS 3.2。 

通过http://opensource.adobe.com/wiki/display/blazeds/Downloads地址下载Binary Distribution版本的BlazeDS软件。该版本为最简版本,解压后只包含一个blazeds.war文件。在创建Flex项目时,为了在项目中通过BlazeDS使用Remoting技术,需要定位blazeds.war文件。 

Ø 在MyEclipse 7.5中配置Tomcat和JDK。 

3.开发基于Remoting技术的Flex应用程序

开发基于Remoting技术的Flex应用程序步骤如下: 

Ø 启动MyEclipse 7.5,单击右上角的【Flash】按钮,切换至Flex应用开发视图。选择“file’

à“new”à“Flex项目”。弹出“新建Flex项目”对话框,按提示进行操作,如图5.1.5所示。


图5.1.5 新建Flex项目

Ø 在图5.1.5中单击“Next”按报,弹出“配置J2EE服务器”对话框,按提示进行操作, 如图5.1.6所示。 


图5.1.6 配置J2EE服务器

Ø 在图5.1.6中单击“Next”按钮,弹出“新建Flex项目”对话框,为Flex项目设置捌径,按提示进行操作,如图5.1.7所示。 


图5.1.7 为Flex项目设置构建路径

Ø 在图5.1.7中,单击【Finish】按钮,完成创建Flex项目的操作,生成的项目文件结构如图5.1.8所示。


图5.1.8 Flex项目文件结构图

Ø 编辑生成的remotingApp.mxml文件,向其中添加以下代码: 

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

   xmlns:s="library://ns.adobe.com/flex/spark"

   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">

<fx:Declarations>

<!-- 将非可视元素(例如服务、值对象)放在此处 -->

</fx:Declarations>

<s:Panel width="564" height="304" verticalCenter="74" horizontalCenter="17" title="Flex Remoting示例">


<s:Rect left="0" right="0" bottom="0" height="30">

<s:fill>

<s:SolidColor color="#E2EDF7"/>

</s:fill>

</s:Rect>

<s:Button id="btnLoad" x="465" y="242" label="加载远程数据 "/>

<mx:AdvancedDataGrid id="adg1"

 designViewDataType="flat" width="562" height="241" x="0" y="0">

<mx:columns>

<mx:AdvancedDataGridColumn headerText="商品编号 " dataField="shopId"/>

<mx:AdvancedDataGridColumn headerText="商品名称 " dataField="shopName"/>

<mx:AdvancedDataGridColumn headerText="商晶单价 " dataField="price"/>

<mx:AdvancedDataGridColumn headerText="商品类别 " dataField="catalog"/>

</mx:columns>

</mx:AdvancedDataGrid>

</s:Panel>

</s:Application>

Ø 将remotingApp项目发布到Tomcat容器中,然后选中“remotingApp.mxml”,单击右键选择“Run As”à“Web应用程序”,运行Flex应用程序,结果如图5.1.9所示。


图5.1.9 运行Flex应用


Ø 单击MyEclipse右上角的【MyEclipse】按钮,切换至Java应用开发视图,创建POJO类,命名为“Shoplnfo.java”。 

package com.soft.flex.pojo;

public class ShopInfo

{

private String shopId;//商品编号

private String shopName;//商品名称private double price;//商品单价

private String catalog;//商品类别

private double price;//商品价格

public ShopInfo (String shopId, String shopName, double price, String catalog) {

super();

this.shopId=shopId;

this.shopName= shopName;

this.price=price;

this.catalog=catalog;

}

public ShopInfo() {

super();

// TODO Auto-generated constructor stub

}

public String getShopId() {

return shopId;

}

public void setShopId(String shopId) {

this.shopId = shopId;

}

public String getShopName() {

return shopName;

}

public void setShopName(String shopName) {

this.shopName = shopName;

}

public String getCatalog() {

return catalog;

}

public void setCatalog(String catalog) {

this.catalog = catalog;

}

public double getPrice() {

return price;

}

public void setPrice(double price) {

this.price = price;

}



}

Ø 继续创建业务类,命名为“Service.java”,在其中定义getAllShop方法,用于从数据库中获取商品列表并返回商品信息。 

public class Service {

public List<ShopInfo> getAllShop(){

//访问数据库代码

List<ShopInfo> shops = new ArrayList<ShopInfo>();

shops.add(new ShopInfo("S001", "彩电", 1200, "家电"));

shops.add(new ShopInfo("S002", "空调", 1300, "家电"));

shops.add(new ShopInfo("S003", "牛奶", 4.5, "饮料"));

shops.add(new ShopInfo("S004", "可口可乐", 3.8, "饮料"));

return shops;

}

}

Ø 在remoting-config.xml文件中配置远程调用类Service。使用记事本打开该文件,在根节点中添加一个子节点。 

<destination id="service">

<properties>

<source>com.soft.flex.service.Service</source>

</properties>

</destination>

在remoting-config.xml文件中可以配置多个destination ,每个节点代表一个远程调用类,使用id属性加以标识 其值不可重复。 Source代表class文件路径。

Ø 修改remotingApp.mxml文件代码,在其中创建RemoteObject对象,并使用该对象访问远程调用类Service.修改后的代码如下: 

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

   xmlns:s="library://ns.adobe.com/flex/spark"

   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">

<fx:Script>

<![CDATA[

import mx.controls.Alert;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;

//处理返回结果时间

protected function shopRemotingObject_resultHandler(event:ResultEvent):void

{

//获取结果并显示

this.adg1.dataProvider = event.result;

}


//处理访问错误异常信息

protected function shopRemotingObject_faultHandler(event:FaultEvent):void

{

//显示错误信息

Alert.show(event.fault.faultString,"错误");

}



protected function btnLoad_clickHandler(event:MouseEvent):void

{

this.shopRemotingObject.getAllShop();

}


]]>

</fx:Script>

<fx:Declarations>

<s:RemoteObject id="shopRemotingObject" destination="service" showBusyCursor="true"

endpoint="http://localhost:8080/remotingApp/messagebroker/amf"

result="shopRemotingObject_resultHandler(event)"

fault="shopRemotingObject_faultHandler(event)">


</s:RemoteObject>

</fx:Declarations>

<s:Panel width="564" height="304" verticalCenter="74" horizontalCenter="17" title="Flex Remoting示例">


<s:Rect left="0" right="0" bottom="0" height="30">

<s:fill>

<s:SolidColor color="#E2EDF7"/>

</s:fill>

</s:Rect>

<s:Button id="btnLoad" x="465" y="242" label="加载远程数据 " click="btnLoad_clickHandler(event)"/>

<mx:AdvancedDataGrid id="adg1"

 designViewDataType="flat" width="562" height="241" x="0" y="0">

<mx:columns>

<mx:AdvancedDataGridColumn headerText="商品编号 " dataField="shopId"/>

<mx:AdvancedDataGridColumn headerText="商品名称 " dataField="shopName"/>

<mx:AdvancedDataGridColumn headerText="商晶单价 " dataField="price"/>

<mx:AdvancedDataGridColumn headerText="商品类别 " dataField="catalog"/>

</mx:columns>

</mx:AdvancedDataGrid>

</s:Panel>

</s:Application>

Ø 重新发布remotingApp,再次运行remotingApp.mxml文件,单击“加载远程数据”按钮, Flex会调用远程Java类查询数据库的商品信息并显示出来,如图5.1.10所示。


图5.1.10

1.3 BlazeDS的序列化机制

    为了传输对象,BlazeDS和Flex提供了客户端ActionScript对象和服务器端Java对象之间的序列化功能;对于Web Service,也提供了客户端ActionScript对象和SOAP对象之间的序列化功能。

    传输对象时,我们一般会选用AMF3来进行编码。AMF3是Adobe在Flash Player 9之后引入的一种用于序列化ActionScript对象的压缩的二进制格式。由于它非常紧凑,而且支持广泛的数据类型和复杂的对象关系,所以在企业应用中被大量使用。

    本节以AMF3编码规则为例,介绍ActionScript对象和Java对象之间的序列化机制。

    1.元标记RemoteClass和Transient

    Flex提供了两个元标记来帮助序列化。RemoteClass是修饰类的编译期的元标记,它[[RemoteClass(alias=””)]的形式定义在ActionScript类前,用于显式映射其修饰的类和远程类编译器在遇到[RemoteClass]元标记时,会在其所在应用(Application)或模块(Module)的初始化代码中插入flash.net.registerClas sAlias( aliasName,classObject)调用以注册ActionScript类及其远程别名。而Transient则是运行期元标记,修饰类的成员变量,用于表明成员变量是瞬态变量,不参与序列化。

    有两点必须注意:其一,使用[RemoteClass]修饰的ActionScript类必须在代码中被引用或使用(也就是说,不是孤立的类),否则,从Java对象无法转换到期望的ActionScript对象。编译成library的类没有此限制,因为不论其是否被引用,FlexBuilder都会编译它;其二,除了[Transient]修饰的成员变量外,非公开变量或属性、只读属性(只有get访问函数)和静态变量属性也不参与序列化,这一点对于ActionScript和Java类是一致的。

  示例5.1给出了两者的使用范例,代码第4行通过[RemoteClass]元标记显式地将自定义ActionScript静态对象Employee映射到同名的Java对象。代码第10行使用了元标记[Transient]修饰Employee的成员变量age。

  经过这两个元标记修饰后,当Flex调用远程方法传递Employee时,BlazeDS会将其转换成同名的Java对象,但age变量会被Flex序列化机制忽略,反之亦然。


示例5.1 Employee.as

   package  com. flexbook.blazeds

     {

             [ Bindable ]

               [ Remo teClas s ( alias = " cam. flexbook.blazeds .Employee " )  ]

    public class Employee

           {

       public var name:String;

       public var code:String;

       public var birthday:Date;

      [Transientl

     public var age:uint;

    }

 }


  2.从ActionScript对象到Java对象

  当Flex应用程序通过RemoteObject调用远程Java方法时,方法的参数会被自动从ActionScript对象转换成Java对象。这个过程经历了两个阶段,首先,Flash Player将ActionScript对象编码成AMF3格式,然后,BlazeDS将AMF3格式的数据流转换成Java对象。

  ActionScript中有些类型,如int、Boolean和String,与Java类型精确匹配,而uint和Number 则没有相应的Java类型与之对应。表4-1-1列出了从ActionScript对象转换到Java对象时的类型对应关系。

表4-1-1  ActionScript对象转换到Java对象时的类型对应关系

ActionScript 类型 (AMF 3)

反序列化为 Java

支持的 Java 类型绑定

Array(密集)

java.util.List


java.util.Collection, Object[ ] (本机数组)

如果类型是一个接口,则会映射到下面的接口实现:

List 变为 ArrayList

SortedSet 变为 TreeSet

Set 变为 HashSet

Collection 变为 ArrayList

自定义 Collection 实现的新实例会绑定到该类型。


Array(稀疏)

java.util.Map

java.util.Map


Boolean

字符串"true"或"false"


java.lang.Boolean

Boolean、boolean 和 String

Flash.utils.ByteArray

byte []


Flash.utils.IExternalizable

java.io.Externalizable


Date


java.util.Date

(已设置为协调世界时 (UTC) 格式)


java.util.Date、java.util.Calendar、java.sql.Timestamp、java.sql.Time 和 java.sql.Date

int/uint

java.lang.Integer

java.lang.Double、java.lang.Long、java.lang.Float、java.lang.Integer、java.lang.Short、java.lang.Byte、java.math.BigDecimal、java.math.BigInteger、String,以及基元类型 double、long、float、int、short 和 byte

Null

null

基元

Number

java.lang.Double


java.lang.Double、java.lang.Long、java.lang.Float、java.lang.Integer、java.lang.Short、java.lang.Byte、java.math.BigDecimal、java.math.BigInteger、String、0(零)

如果发送了 null,则为基元类型 double、long、float、int、short 和 byte


Object(泛型)

java.util.Map

如果指定了 Map 接口,则为 java.util.Map 创建一个新的 java.util.HashMap,为 java.util.SortedMap 创建一个新的 java.util.TreeMap。

String

java.lang.String

java.lang.String、java.lang.Boolean、java.lang.Number、java.math.BigInteger、java.math.BigDecimal、char[]、以及任何基元数字类型

有类型对象


有类型对象

在使用 [RemoteClass] 元数据标签指定远程类名称时。Bean 类型必须具有公共的无参数构造函数。


有类型对象

undefined

null

null(对于对象)和默认值(对于基元)

XML

org.w3c.dom.Document

org.w3c.dom.Document


XMLDocument

(旧 XML 类型)


org.w3c.dom.Document


org.w3c.dom.Document

可以针对在 services-config.xml 文件中定义的任何通道启用对于 XMLDocument 类型的旧 XML 支持。此设置仅在将数据从服务器发回到客户端时很重要,它控制 org.w3c.dom.Document 实例如何发送到 ActionScript。



  当然,BlazeDS在Java对象中寻找合适的方法签名时会尝试对Java类型做出兼容的转换。比如,Flex应用在调用远程方法时传人一个int类型的参数,但远程Java对象只有一个接受参数的方法,这时,BlazeDS将尝试将这个int转换成java.lang.String,然后再调用方法。

  ActionScript中的Array允许两种方式索引元素,严格数组(strict array)使用数字作为索引,索引代表了元素的排列位置,关联数组(associative array)使用字符串作为索引,索引代表了元素的名称。一个数组中只要有一个元素使用字符串作为索引,那么它就是关联数组,这时,数组实际上退化成了ActionScript的动态对象。在严格数组中,我们把索引不是从0开始或者索引不连续的数组称为稀疏数组。关联数组通过序列化将转换成java.util.Map,稀疏数组也被转换成java.util.Map以避免传递大量null元素。

对于ActionScript的String类型,由于可以匹配的Java的String类型,因此优先转换成字符串,但如果远程Java对象中没有方法的签名能够匹配,BlazeDS将尝试将字符串转换成Boolean(如果字符串是true,false)或数值类型(如果字符串表示一个数值)。

如果将ActionScript的null 或者undefined传给远程Java方法,他将会被转化成null(如果目标类型是java.lang.Object或其子类)或转换成基本类型的默认值(如果目标类型是Java中的基本类型)。

3 从Java对象到ActionScript对象

当服务器需要返回Java对象时,BlazeDS会将Java对象编码成AMF3格式,并序列化到Flex应用端,Flex应用解析AMF3格式的流数据生成ActionScript对象。表4-1-2列出了Java对象转换成ActionScript对象的类型对应关系。

表4-1-2 Java对象转换成ActionScript对象的类型对应关系。


Java 类型

ActionScript 类型 (AMF 3)

java.lang.String

String

java.lang.Boolean, boolean

Boolean

java.lang.Integer, int


int

如果值小于 0xF0000000 且大于 0x0FFFFFFF,则会按照 AMF 编码要求将值提升为 Number。


java.lang.Short, short


int

如果 i 小于 0xF0000000 且大于 0x0FFFFFFF,则会将值提升为 Number。


java.lang.Byte, byte[]


int

如果 i 小于 0xF0000000 且大于 0x0FFFFFFF,则会将值提升为 Number。


java.lang.Byte[]

flash.utils.ByteArray

java.lang.Double, double

Number

java.lang.Long, long

Number

java.lang.Float, float

Number

java.lang.Character, char

String

java.lang.Character[], char[]

String

java. math.BigInteger

String

java.math.BigDecimal

String

java.util.Calendar


Date

日期按照协调世界时 (UTC) 时区的时间进行发送。客户端和服务器必须根据时区相应地调整时间。


java.util.Date


Date

日期按照 UTC 时区的时间进行发送。客户端和服务器必须根据时区相应地调整时间。


java.util.Collection(例如,java.util.ArrayList)

mx.collections.ArrayCollection

java.lang.Object[]

Array

java.util.Map

Object(无类型)。例如,将 java.util.Map[] 转换为对象的 Array。

java.util.Dictionary

Object(无类型)

org.w3c.dom.Document

XML 对象

Null

null

java.lang.Object(以前列出的类型除外)


有类型 Object

通过使用 JavaBean 内部检查规则将对象进行序列化,并且对象包括公共字段。不包括静态字段、瞬态字段、非公共字段,以及非公共 bean 属性或静态 bean 属性。



如果没有使用[RemoteClass]标签,则转换成动态对象,否则转换成自定义的静态对象。

4.自定义序列化机制

 以上讨论的是BlazeDS的标准序列化机制。如果标准规则不能满足要求,BlazeDS还提供了扩展机制,允许编写代码自定义序列化规则。在Flex端,我们可以使目标类实现接口flash.net.IExternalizable,在Java端实现接口java.io.Externalizable。

  自定义序列化机制有很多应用场景,比如压缩数据、隐藏敏感数据等。示例5.2定义了一个DataRow类,它是所有数据行对象的基类,每个数据行都有一个唯一标示符rowID,通过rowID客户端和服务器端可以识别它们操作的对象,通常,我们期望rowID由服务器端负责生成,并且一旦分配给对象就不能被外部更改,因此它需要被定义成只读属性。而BlazeDS标准的序列化机制是不序列化只读属性的,但rowID是如此重要,以至于如果不传递给客户端,那么在客户端处理完DataRow后,服务器端就不知道是哪个DataRow对象被处理了。

  自定义序列化机制可以帮助实现我们的愿望:让DataRow实现接口IExternalizable,然后在WriteExternal和readExternal中分别向序列化流写入和从序列化流读出rowID。这样,即使我们将rowID定义成只读属性,丝毫不影响rowID的序列化。

示例5.2DataRow.as

package com.flexbook.blazeds

 {

   import flash.utils.IDataInput;

   import  flash.utils .IDataOutput ;

   import   flash.utils .IExternalizable ;


  [ RemoteClass ( alias = " cam. flexbook.blazeda .DataRow " ) ]

public class DataRow implemente IExternalizable

 {

  private var _rowID:Object;

  public function DataRow() {

         }

   public function get rowID():Object{

       return _rowID;

         }

   public function writeExternal(output:IDataOutput)

               output.writeObject (_rowID) ;

        }

    public function readExternal(input:IDataInput) {

                 rowID=input.readObject ( ) ,


 }

}

    flash.utils.IDataInput和flash.utils.IDataOutput代表了序列化的输入流和输出流,当Flex序列化对象时,会调用对象的writeExternal,并传入IDataOutput以便对象输出其属性;当Flex反序列化对象时,则调用对象的readExternal,并传入IDatalnput以便对象从流中读取其属性IDataInput和IDataOutput提供了读取和写入各种类型ActionScript对象的函数,来帮助我们序列化和反序列化。

    示例5.3是DataRow对象在服务器端的定义,它与前端代码基本相似,唯一不同的是它有两个构造函数,默认构造函数用于反序列化(因为反序列化必须要有无参构造函数),:另一构造函数用于服务器端创建DataRow对象时为它指定rowID(这也是可以修改rowID的唯一的机会)。


示例5.3  DataRow.java



public class DataRow implements Externalizable{


private Object rowID,

    public  DataRow()  {

    super();

    )    

    public DataRow(Object rowID)  {

    super();

    this.rowID=rowID;

    )

    public  Object  getRowID()(

    return rowID;

    )

    public void readExternal(ObjectInput in) throws IOException,

    ClassNotFoundException {

    rowID=in.readObject();

    )

    public voicl writeExternal(ObjectOutput.ut) throws IOException  {

    out.writeObiect(rowID);

    )

    )


    如此,我们就得到了一个安全的DataRow对象,除了构造时可以为它分配rowID,其他时毫无论在服务器端还是客户端都无法对rowID进行修改。从它继承的类都可以获得这项好处,前提是服务器端的Java类和客户端的ActionScript都需要继承相应的DataRow。

1.4 BlazeDS和Spring整合

如果需要BlazeDs与Spring框架整合使用,是非常简单的事情,因为,SpringSource和Adobe已经合作为BlazeDS提供了Spring支持,即Spring BlazeDS Integration,关于这个项目的更多信息可以访问​​​​​。

Spring BlazeDS Integration 是 SpringSource 的开源项目,用于整合 Spring 与 BlazeDS。不使用 Spring BlazeDS Integration 同样可以整合 Spring 与 BlazeDS。但这种整合方式不自然,需要额外维护一个 BlazeDS 配置文件,Spring BlazeDS Integration 会改善这种处境。

Spring BlazeDS Integration 需要的软件环境:

Ø Java 5 或更高

Ø Spring 2.5.6 或更高

Ø BlazeDS 3.2 或更高

Spring BlazeDS Integration 特征

Ø MessageBroker(BlazeDS 的核心组件)被配置为 Spring 管理的 Bean

Ø Flex 客户端发出的 HTTP 消息通过 Spring 的 DispatcherServlet 路由给 MessageBroker

Ø Remote objects 以 Spring 的方式配置在 Spring 配置文件内


下面我们演示BlazeDS和Spring的整合。

(1)准备所需 jar 包

下载 Spring Framework dependencies和Spring BlazeDS Integration解压备用,在项目中添加Spring支持,并将以下 2 部分 jar 包拷贝到项目的 lib 下:

Ø Spring Framework dependencies

org.aopalliance 内的 com.springsource.org.aopalliance-1.0.0.jar

edu.emory.mathcs.backport 内的 com.springsource.edu.emory.mathcs.backport-3.0.0.jar

net.sourceforge.cglib 内的 com.springsource.net.sf.cglib-2.2.0.jar

Ø Spring BlazeDS Integration

org.springframework.flex-1.0.3.RELEASE.jar

(2):修改 web.xml 文件

将 web.xml 内所有 Flex 相关配置删除掉,添加以下内容(改用 Spring web 应用的前端控制器处理所有应用请求)


<servlet>

  <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

  <init-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>/WEB-INF/web-application-config.xml</param-value>

  </init-param>

  <load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

  <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

  <url-pattern>/messagebroker/*</url-pattern>

</servlet-mapping>

(3):配置 web-application-config.xml

1)创建应用上下文配置文件 web-application-config.xml


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">


</beans>

2)为了使用 Spring BlazeDS Integration 的 tag,增加命名空间


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:flex="http://www.springframework.org/schema/flex"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://www.springframework.org/schema/flex       http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">


</beans>

3)为了把请求路由给 MessageBroker,添加以下 tag


<flex:message-broker />

4)定义 Bean,并用 remoting-destination tag 把它暴露给 Flex


<bean id="employeeServiceDest" class="com.sample.EmployeeService">

  <flex:remoting-destination />

</bean>


至此BlazeDS与Spring的整合就完成了。


1.5 BlazeDS的消息服务

BlazeDS消息服务(Message Service )提供发布(publish)/订阅(subscribe)机制,允许 Flex 应用程序发布消息、订阅消息终端(messaging destination),从而实现实时数据的推动和协作传送。


(1)Message Service


Message Service 提供发布(publish)/订阅(subscribe)机制允许Flex 应用 程序发布消息、订阅消息终端(messaging destination),从而实现数据的实时 推动和协作传送。

消息终端在messaging-config.xml配置,其中频道(channel)是其关键元素, 它用来实现客户端和服务器端交换数据。使用BlazeDS,消息终端通常用作 streaming频道或者polling频道。

使用streaming频道,服务器端会一直响应HTTP请求直到该频道连接被关闭, 它允许服务器向客户端不断传送大量的数据。因为HTTP连接是独一无二的,这实 现数据的双向传送,每个streaming AMF或者HTTP频道事实上需要两个浏览器 HTTP连接, 一个连接需要不断处理服务器端与频道紧密相关的客户端的响应。 另外需要一个短暂连接,只有当数据需要传送到服务器时,它才脱离浏览器连接 池;当短暂连接不再需要时,它立即被释放回浏览器连接池。

polling频道可以通过简单的时间间隔或者使用服务器等待来配置,如果数据 不马上可用 (长轮循)的话。另外,每次轮循响应完成请求。默认下浏览器HTTP 1.1的连接是持续的,浏览器轮循已有的连接,发送并发的轮循请求,以此来减 轻轮循的开销。

当需要准实时通信时,streaming 频道是最好选择。


(2)IE 与 Firefox浏览器下的不同

浏览器对每个session都有连接数限制。不同的浏览器,连接最大数以及对 session的处理方式都不一样。

IE中每个session的最大连接数为2。 但如果从开始菜单或快捷方式打开多个 IE实例,每个IE实例开启不同的进程并拥有各自session。另外,如果我们通过 CTRL+N 开启对已有的IE实例一个新的IE窗口,该窗口将与创建它的IE实例共用 一个session 。也就是说,如果程序实例开启不同的进程,我们可以通过HTTP streaming建立不限量应用取得服务器端数据;如果通过CTRL+N开启多个窗口, 每个session最多建立2个连接。

Firefox中每个session最多建立8个连接。如果从开始菜单或快捷方式打开多 个Firefox实例,所有实例开启使用同一进程并共用一个session。既然浏览器对 普通的HTTP请求通常只需要一个连接, 理论上我们可以最多可以建立7个HTTP streaming连接。


(3)messaging-config.xml

另外,如果每个session到达最大连接数,使用streaming channel连接到服务器的下一次尝试将失败并抛出以下异常:

Endpoint with id 'my-streaming-amf' cannot grant streaming connection to FlexClient with id 'D640B86F-6B1D-92DF- 8288-1B737A371AFE' because max-streaming-connections-per-session limit of '1' has been reached。

不过,BlazeDS提供一种优雅的退后机制来处理这种情况:

客户端始终会尝试使用频道表(messaging-config.xml中为服务终端定义) 中的第一个频道来连接。如果该连接失败, 客户端将自动退后到频道表中的下一频道。我们可以为所有的服务终端定义了如下默认的ChannelSet:


<default-channels>

<channel ref="my-streaming-amf"/>

<channel ref="my-polling-amf"/>

</default-channels>


也就是说,客户端应用会首先尝试使用streaming channel连接,如果连接失 败会使用polling channel。


在客户端,Flex提供了 Producer和Consumer这两个组件,让你用来向目标地址发送或订阅消息。如果要订阅消息,你就使用Consumer类的 subscribe()方法。当有消息发送到你订阅了的目标地址时,Consumer上就会触发message事件。

示例5.4

客户端代码:

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

   xmlns:s="library://ns.adobe.com/flex/spark"

   creationComplete="consumer.subscribe();"

   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">

<fx:Script>

<![CDATA[

import mx.controls.Alert;

import mx.messaging.events.MessageFaultEvent;

import mx.messaging.messages.AsyncMessage;

import mx.messaging.messages.IMessage;

private function send():void  

{   

   var message:IMessage = new AsyncMessage();   

   message.body.chatMessage = msg.text+consumer.clientId;   

    producer.send(message);   

    msg.text = "";   


  }   


   private function messageHandler(message:IMessage):void  

   {   

    log.text += message.body.chatMessage + "\n";    

   }   



protected function consumer_faultHandler(event:MessageFaultEvent):void

{

Alert.show(event.faultDetail);

}



protected function producer_faultHandler(event:MessageFaultEvent):void

{

Alert.show(event.faultDetail);

}


]]>

</fx:Script>

<fx:Declarations>

<mx:ChannelSet id="cs">   

  <mx:StreamingAMFChannel url="http://localhost:8400/MsgService/messagebroker/streamingamf"/>   


  </mx:ChannelSet>   

  <mx:Producer id="producer"  fault="producer_faultHandler(event)" destination="chat" channelSet="{cs}"/>   

  <mx:Consumer id="consumer" destination="chat" channelSet="{cs}" message="messageHandler(event.message)" fault="consumer_faultHandler(event)"/>   


</fx:Declarations>


<s:Panel title="Chat Test!"   x="20" y="19" width="518" height="295">   

  <s:TextArea id="log" x="19" y="11" width="473" height="166"/>   


  <s:TextInput id="msg" x="19" y="191" width="377" height="46" enter="send()"/>   

  <s:Button x="411" y="192" label="发送消息" height="43" width="77" click="send()"/>   


 </s:Panel>   


</s:Application>


服务器端services-config.xml定义Streaming通道:

<?xml version="1.0" encoding="UTF-8"?>

<service id="message-service"

    class="flex.messaging.services.MessageService">


    <adapters>

        <adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />

       <adapter-definition id="jms" class="flex.messaging.services.messaging.adapters.JMSAdapter"/>

    </adapters>




    <!--   <default-channels>

        <channel ref="my-polling-amf"/>

    </default-channels>-->

<destination id="chat">   


        <properties>   

           <network>   

                <session-timeout>0</session-timeout>   

            </network>   

            <server>   

               <max-cache-size>1000</max-cache-size>   

                <message-time-to-live>0</message-time-to-live>   

                <durable>false</durable>   

           </server>   

        </properties>   

        <channels>   

        <channel ref="my-streaming-amf" />   

        </channels>   

    </destination>   


</service>


服务器端messaging-config.xm中定义目标并指定通道:

<?xml version="1.0" encoding="UTF-8"?>

<services-config>


    <services>

        <service-include file-path="remoting-config.xml" />

        <service-include file-path="proxy-config.xml" />

        <service-include file-path="messaging-config.xml" />        




      </services>

    <security>

        <login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"/>

        <!-- Uncomment the correct app server

        <login-command class="flex.messaging.security.TomcatLoginCommand" server="JBoss">

<login-command class="flex.messaging.security.JRunLoginCommand" server="JRun"/>        

        <login-command class="flex.messaging.security.WeblogicLoginCommand" server="Weblogic"/>

        <login-command class="flex.messaging.security.WebSphereLoginCommand" server="WebSphere"/>

        -->


        <!--

        <security-constraint id="basic-read-access">

            <auth-method>Basic</auth-method>

            <roles>

                <role>guests</role>

                <role>accountants</role>

                <role>employees</role>

                <role>managers</role>

            </roles>

        </security-constraint>

         -->

    </security>


    <channels>


 <channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">   

            <endpoint url="http://localhost:8400/MsgService/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>   

            <properties>   

               <idle-timeout-minutes>0</idle-timeout-minutes>   

                <max-streaming-clients>50</max-streaming-clients>   

                <server-to-client-heartbeat-millis>5000</server-to-client-heartbeat-millis>   

               <user-agent-settings>   

                    <user-agent match-on="MSIE" kickstart-bytes="2048" max-streaming-connections-per-session="1"/>   

                   <user-agent match-on="Firefox" kickstart-bytes="2048" max-streaming-connections-per-session="1"/>   

                </user-agent-settings>   

           </properties>   

        </channel-definition>

        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">

            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>

        </channel-definition>


        <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">

            <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/>

            <properties>

                <add-no-cache-headers>false</add-no-cache-headers>

            </properties>

        </channel-definition>


        <channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">

            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>

            <properties>

                <polling-enabled>true</polling-enabled>

                <polling-interval-seconds>4</polling-interval-seconds>

            </properties>

        </channel-definition>



        <channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel">

            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/http" class="flex.messaging.endpoints.HTTPEndpoint"/>

        </channel-definition>

  <!--

        <channel-definition id="my-secure-http" class="mx.messaging.channels.SecureHTTPChannel">

            <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/httpsecure" class="flex.messaging.endpoints.SecureHTTPEndpoint"/>

            <properties>

                <add-no-cache-headers>false</add-no-cache-headers>

            </properties>

        </channel-definition>

        -->

    </channels>


    <logging>

        <target class="flex.messaging.log.ConsoleTarget" level="Error">

            <properties>

                <prefix>[BlazeDS] </prefix>

                <includeDate>false</includeDate>

                <includeTime>false</includeTime>

                <includeLevel>false</includeLevel>

                <includeCategory>false</includeCategory>

            </properties>

            <filters>

                <pattern>Endpoint.*</pattern>

                <pattern>Service.*</pattern>

                <pattern>Configuration</pattern>

            </filters>

        </target>

    </logging>


    <system>

        <redeploy>

            <enabled>false</enabled>

            <!--

            <watch-interval>20</watch-interval>

            <watch-file>{context.root}/WEB-INF/flex/services-config.xml</watch-file>

            <watch-file>{context.root}/WEB-INF/flex/proxy-config.xml</watch-file>

            <watch-file>{context.root}/WEB-INF/flex/remoting-config.xml</watch-file>

            <watch-file>{context.root}/WEB-INF/flex/messaging-config.xml</watch-file>

            <watch-file>{context.root}/WEB-INF/flex/data-management-config.xml</watch-file>

            <touch-file>{context.root}/WEB-INF/web.xml</touch-file>

             -->

        </redeploy>

    </system>


</services-config>

运行应用效果如图5.1.11所示,轻松实现了数据的推送:



图5.1.11 Flex数据推送


任务实训部分 


实训任务1:使用HttpServcie方式与后台通信

训练技能点

HttpServcie

需求说明

使用HttpService对象开发Flex应用程序 按条件查询数据库中某张表的数据并显示在表格中。

实现思路

(1)创建Flex项目,将此项目的服务器技术选择为J2EE服务器(无需使用远程对象访问服务)。

(2)切换到MyEclipese java 开发视图,创建一个POJO类用于描述奥运会各个国家获得的奖牌情况。

package com.soft.flex.flex4sj.pojo;


public class Cup {

private int id;

//国家代号

private String countryId;

//国家名称

private String countryName;

//金牌数

private int goldMedal;

//银牌数

private int silverMedal;

//铜牌数

private int bronzeMedal;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getCountryId() {

return countryId;

}

public void setCountryId(String countryId) {

this.countryId = countryId;

}

public String getCountryName() {

return countryName;

}

public void setCountryName(String countryName) {

this.countryName = countryName;

}

public int getGoldMedal() {

return goldMedal;

}

public void setGoldMedal(int goldMedal) {

this.goldMedal = goldMedal;

}

public int getSilverMedal() {

return silverMedal;

}

public void setSilverMedal(int silverMedal) {

this.silverMedal = silverMedal;

}

public int getBronzeMedal() {

return bronzeMedal;

}

public void setBronzeMedal(int bronzeMedal) {

this.bronzeMedal = bronzeMedal;

}

public Cup(String countryId, String countryName, int goldMedal,

int silverMedal, int bronzeMedal) {

super();

this.countryId = countryId;

this.countryName = countryName;

this.goldMedal = goldMedal;

this.silverMedal = silverMedal;

this.bronzeMedal = bronzeMedal;

}

public Cup() {

super();

// TODO Auto-generated constructor stub

}


}

(3)创建业务类,在该类 中定义根据国家查询获取奖牌情况的方法。

package com.soft.flex.flex4sj.service;



import java.util.*;


import com.soft.flex.flex4sj.pojo.Cup;


 public class CupService {

private List<Cup> cupList;

//模拟数据库数据

public CupService(){

cupList  = new ArrayList();

Cup cup1 = new Cup("china", "中国",30 ,20 ,10 );

Cup cup2 = new Cup("america", "美国",20 ,23 ,12 );

Cup cup3 = new Cup("japan", "日本",25 ,27 ,15 );

Cup cup4 = new Cup("france", "法国",10 ,18 ,20 );

Cup cup5 = new Cup("russia", "俄罗斯",16 ,30,25 );

Cup cup6 = new Cup("singapore", "新加坡",12 ,25 ,18 );

cupList.add( cup1);

cupList.add( cup2);

cupList.add( cup3);

cupList.add( cup4);

cupList.add( cup5);

cupList.add( cup6);

}

public List getAll(){



return cupList;



}

public Cup getCupByCountryId(String countryId){

Cup c = null;

for(Cup cup : cupList){


if(countryId.equals(cup.getCountryId())){

c = cup;

break;

}

}

return c;




}




}


(4)创建servlet 路径为/query, 该servlet根据传入的cid 调用业务类获取结果,并转化为xml格式返回。

public class QueryServlet extends HttpServlet {



public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doPost(request, response);

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {


response.setContentType("text/html;charset=utf-8");

PrintWriter out = response.getWriter();

//构造xml头标记

String xml="<?xml version=\"1.0\" encoding=\"utf-8\"?><cups>";

     String method = request.getParameter("method");

     CupService service = new CupService();

     //如果是查询所有

     if("all".equals(method)){

     List<Cup> list = service.getAll();




         for(Cup cup : list){


         String cid = cup.getCountryId();

         String cname = cup.getCountryName();

         int gm = cup.getGoldMedal();

         int sm = cup.getSilverMedal();

         int bm = cup.getBronzeMedal();

         //构造xml节点

         xml += "<cup countryId='"+cid+"' countryName='"+cname+"' goldMedal='"+gm+"' silverMedal='"+sm+"' bronzeMedal='"+bm+"'/>";



         }


     }else{

     String countryId =  request.getParameter("cid");

     Cup cup = service.getCupByCountryId(countryId);

     String cid = cup.getCountryId();

     String cname = cup.getCountryName();

     int gm = cup.getGoldMedal();

     int sm = cup.getSilverMedal();

     int bm = cup.getBronzeMedal();


     xml += "<cup countryId='"+cid+"' countryName='"+cname+"' goldMedal='"+gm+"' silverMedal='"+sm+"' bronzeMedal='"+bm+"'/>";



     }






     xml+="</cups>";

     //输出结尾标志

     out.print(xml);

out.flush();

out.close();

}


}


(5)创建MXML界面,通过HttpService对象访问servlet 并获取查询结果。

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

   xmlns:s="library://ns.adobe.com/flex/spark"

   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"

   initialize="application1_initializeHandler(event)"

   >

<fx:Script>

<![CDATA[

import mx.controls.Alert;

import mx.events.FlexEvent;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;


import spark.events.IndexChangeEvent;

private var flag:int;

protected function application1_initializeHandler(event:FlexEvent):void

{

this.flag=0;

this.myhttp.url = "/sj41/query?method=all&cid=all";

this.myhttp.send();

}



protected function myhttp_resultHandler(event:ResultEvent):void

{


//如果是初始化时返回的结果

if(flag==0){

this.ddl.dataProvider=event.result.cups.cup;

this.ddl.labelField = "countryName";

this.adg1.dataProvider=event.result.cups.cup;

}

else{//如果是选择下拉列表返回的结果

this.adg1.dataProvider=event.result.cups.cup;

}

}



protected function myhttp_faultHandler(event:FaultEvent):void

{

Alert.show(event.fault.faultString);

}



protected function ddl_changeHandler(event:IndexChangeEvent):void

{

var cid:String = this.ddl.selectedItem.countryId;

this.flag=1;

this.myhttp.url = "/sj41/query?method=getById&cid="+cid;

this.myhttp.send();

}


]]>

</fx:Script>

<fx:Declarations>

<s:HTTPService id="myhttp"  showBusyCursor="true" result="myhttp_resultHandler(event)" fault="myhttp_faultHandler(event)"/>

</fx:Declarations>

<s:Panel width="557" height="376" title="使用HttpService 与后台通信" horizontalCenter="0" verticalCenter="0">

<mx:AdvancedDataGrid x="2" y="9" id="adg1" designViewDataType="flat" width="545" height="274">

<mx:columns>

<mx:AdvancedDataGridColumn headerText="代号" dataField="countryId"/>

<mx:AdvancedDataGridColumn headerText="国家" dataField="countryName"/>

<mx:AdvancedDataGridColumn headerText="金牌数" dataField="goldMedal"/>

<mx:AdvancedDataGridColumn headerText="银牌数" dataField="silverMedal"/>


<mx:AdvancedDataGridColumn headerText="铜牌数" dataField="bronzeMedal"/>


</mx:columns>

</mx:AdvancedDataGrid>

<s:Label x="216" y="305" text="选择国家:" height="23" verticalAlign="middle"/>



<s:Button x="475" y="305" label="显示全部" click="application1_initializeHandler(event as FlexEvent)"/>

<s:DropDownList x="306" y="305" id="ddl" prompt="请选择国家" change="ddl_changeHandler(event)"></s:DropDownList>

</s:Panel>

</s:Application>


(6)运行应用程序,效果如图5.2.1所示。


图5.2.1 HttpService 示例

实训任务2:使用RemotingObject 与后台通信

训练技能点

RemotingObject。

需求说明

使用RemotingObject 重构任务1。

实现思路:

(1)创建Flex项目,将此项目的服务器技术选择为J2EE服务器(使用远程对象访问服务)。 如图5.2.2所示。


图5.2.2 创建Flex项目

(2)修改WebRoot/WEB-INFO/flex/remoting-config.xml,配置业务类:

<?xml version="1.0" encoding="UTF-8"?>

<service id="remoting-service"

    class="flex.messaging.services.RemotingService">


    <adapters>

        <adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/>

    </adapters>


    <default-channels>

        <channel ref="my-amf"/>

    </default-channels>

<destination id="service">

<properties>

<source>com.soft.flex.flex4sj.service.CupService</source>

</properties>

</destination>

</service> 

(1)修改MXML应用程序 添加RemotingObject对象。

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

   xmlns:s="library://ns.adobe.com/flex/spark"

   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"

   initialize="application1_initializeHandler(event)"

   >

<fx:Script>

<![CDATA[

import mx.collections.IList;

import mx.controls.Alert;

import mx.events.FlexEvent;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;


import spark.events.IndexChangeEvent;

private var flag:int;

protected function application1_initializeHandler(event:FlexEvent):void

{

this.flag=0;

this.myremoting.getAll();

}






protected function ddl_changeHandler(event:IndexChangeEvent):void

{

var cid:String = this.ddl.selectedItem.countryId;

this.flag=1;

this.myremoting.getCupByCountryId(cid);

}



protected function myremoting_resultHandler1(event:ResultEvent):void

{


this.ddl.dataProvider=event.result as IList;

this.ddl.labelField = "countryName";

this.adg1.dataProvider=event.result;



}


protected function myremoting_resultHandler2(event:ResultEvent):void

{


this.adg1.dataProvider=event.result;


}

protected function myremoting_faultHandler(event:FaultEvent):void

{

Alert.show(event.fault.faultString);

}


]]>

</fx:Script>

<fx:Declarations>

<s:RemoteObject id="myremoting" showBusyCursor="true" destination="service"

endpoint="http://localhost:8080/sj42/messagebroker/amf"

fault="myremoting_faultHandler(event)"

>

<s:method name="getAll" result="myremoting_resultHandler1(event)" />

<s:method name="getCupByCountryId" result="myremoting_resultHandler2(event)"/>

</s:RemoteObject>

</fx:Declarations>

<s:Panel width="557" height="376" title="使用HttpService 与后台通信" horizontalCenter="0" verticalCenter="0">

<mx:AdvancedDataGrid x="2" y="9" id="adg1" designViewDataType="flat" width="545" height="274">

<mx:columns>

<mx:AdvancedDataGridColumn headerText="代号" dataField="countryId"/>

<mx:AdvancedDataGridColumn headerText="国家" dataField="countryName"/>

<mx:AdvancedDataGridColumn headerText="金牌数" dataField="goldMedal"/>

<mx:AdvancedDataGridColumn headerText="银牌数" dataField="silverMedal"/>


<mx:AdvancedDataGridColumn headerText="铜牌数" dataField="bronzeMedal"/>


</mx:columns>

</mx:AdvancedDataGrid>

<s:Label x="216" y="305" text="选择国家:" height="23" verticalAlign="middle"/>



<s:Button x="475" y="305" label="显示全部" click="application1_initializeHandler(event as FlexEvent)"/>

<s:DropDownList x="306" y="305" id="ddl" prompt="请选择国家" change="ddl_changeHandler(event)"></s:DropDownList>

</s:Panel>

</s:Application>



运行应用,效果如图5.2.1所示。



实训任务3:RemotingObject 整合Hibernate Spring

训练技能点

Ø RemotingObject。

Ø 整合Spring框架。

需求说明

使用RemotingObject 整合Hibernate Spring 重构任务1。​

实现思路:

(1)创建Flex项目,将此项目的服务器技术选择为J2EE服务器(使用远程对象访问服务)。

(2)创建数据库表tb_cup 表字段与实体类属性对应 如图5.2.3所示。


图5.2.2 tb_Cup表

(3)切换到MyEclipes视图,分别添加hibernate支持和spring支持,并使用逆向工程生成视图类,映射文件等。

(4)创建业务类,在该类 中定义根据国家查询获取奖牌情况的方法。

package com.soft.flex.flex4sj.service;


import java.util.List;


import com.soft.flex.flex4sj.dao.Cup;

import com.soft.flex.flex4sj.dao.CupDao;


public class CupService {

private CupDao dao;


public List<Cup> getAll(){


return dao.findAll();

}

public List<Cup> getCupByCountryId(String cid){


return dao.findByCountryId(cid);

}

public CupDao getDao() {

return dao;

}


public void setDao(CupDao dao) {

this.dao = dao;

}


}


(5)添加整合Spring框架的响应jar包 并在applicationContext.xml中配置业务类。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:flex="http://www.springframework.org/schema/flex"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

​​

​​

​​

​​

​​

         <flex:message-broker />

<bean id="service" class="com.soft.flex.flex4sj.service.CupService">

  <flex:remoting-destination />

  <property name="dao" ref="CupDAO"></property>

</bean>

<!--其他配置省略-->

</beans>

(6)修改工程的web.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>

<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>sj43</display-name>


<context-param>

<param-name>flex.class.path</param-name>

<param-value>/WEB-INF/flex/hotfixes,/WEB-INF/flex/jars</param-value>

</context-param>


<!-- Http Flex Session attribute and binding listener support -->

<listener>

<listener-class>flex.messaging.HttpFlexSession</listener-class>

</listener>


<!-- MessageBroker Servlet -->

<servlet>

  <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

  <init-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>classpath:applicationContext.xml</param-value>

  </init-param>

  <load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

  <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

  <url-pattern>/messagebroker/*</url-pattern>

</servlet-mapping>

<welcome-file-list>

<welcome-file>index.html</welcome-file>

<welcome-file>index.htm</welcome-file>

<welcome-file>index.jsp</welcome-file>

<welcome-file>default.html</welcome-file>

<welcome-file>default.htm</welcome-file>

<welcome-file>default.jsp</welcome-file>

</welcome-file-list>



</web-app>

(7)修改MXML代码如下:

<s:RemoteObject id="myremoting" showBusyCursor="true" destination="​service​"

endpoint="​​​​"


fault="myremoting_faultHandler(event)"

>

其中destination=“service” service要与applicationContext.xml中业务bean的id一致。

运行应用程序,效果如图5.2.1.所示(注意要在服务器中将重复的cglib.jar删去)。

实训任务4:实现分页

训练技能点

Ø RemotingObject。

Ø 整合Spring框架。

需求说明

在任务3的基础上实现分页功能。

实现步骤:

(1)切换到MyEclipse视图 创建Page.java用来封装分页数据。

import java.util.List;


public class Page {

private List data;

private int currentPage;

private int totalPage;

private int pageSize;

private int totalClum;

public List getData() {

return data;

}


public void setData(List data) {

this.data = data;

}

public int getCurrentPage() {

return currentPage;

}

public void setCurrentPage(int currentPage) {

this.currentPage = currentPage;

}

public int getTotalPage() {

return totalPage;

}

public void setTotalPage(int totalPage) {

this.totalPage = totalPage;

}

public int getPageSize() {

return pageSize;

}

public void setPageSize(int pageSize) {

this.pageSize = pageSize;

}

public int getTotalClum() {

return totalClum;

}

public void setTotalClum(int totalClum) {

this.totalClum = totalClum;

this.totalPage=( totalClum%pageSize==0  ?totalClum/pageSize:totalClum/pageSize+1);


}


}


(2)在dao类中添加分页查询的方法

public List findByPage(Page page) {


        String hql = "from Cup";

        try {

         Session session = getSession();

         Query query = session.createQuery(hql);

         query.setFirstResult((page.getCurrentPage()-1)*page.getPageSize());

         query.setMaxResults(page.getPageSize());

         List list = query.list();

         releaseSession(session);

         return list;


        } catch (RuntimeException re) {

            log.error("delete failed", re);

            throw re;

        }

    }

public int count() {

        log.debug("deleting User instance");

        String hql = "select count(cup) from Cup as cup";

        try {

         Session session = getSession();

         Query query = session.createQuery(hql);


         long l = (Long)query.uniqueResult();

         return (int)l;


        } catch (RuntimeException re) {

            log.error("delete failed", re);

            throw re;

        }

    }

(3)在service类中添加相应的方法。

public Page findByPage(Page page){


List data= dao.findByPage(page);

int count = dao.count();

page.setData(data);

page.setTotalClum(count);

return page;

}


(4)切换回Flash视图 开发与Java端Page类对应的as类。

package com.oa.vo

{

import mx.collections.ArrayCollection;

[RemoteClass(alias="com.soft.flex.flex4sj.dao.Page")]

public class Page

{

public function Page()

{

}

public var data :ArrayCollection;

public var  currentPage:int;

public var  totalPage:int;

public var  pageSize:int;

public var  totalClum:int;


}

}



(5)修改主程序的MXML文件 添加分页栏 。

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

   xmlns:s="library://ns.adobe.com/flex/spark"

   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"

   initialize="application1_initializeHandler(event)"

    xmlns:ns1="*">

<fx:Script>

<![CDATA[

import com.oa.vo.Page;


import mx.collections.IList;

import mx.controls.Alert;

import mx.events.FlexEvent;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;


import spark.events.IndexChangeEvent;

[Bindable]

private var pageData:Page;

public  function loadData(currentPage:int):void{


var page:Page = new Page();

page.currentPage=currentPage;

page.pageSize=this.setPageSize.selectedItem.data;

this.myremoting.findByPage(page);

}

protected function application1_initializeHandler(event:FlexEvent):void

{


loadData(1);

}






protected function ddl_changeHandler(event:IndexChangeEvent):void

{

var cid:String = this.ddl.selectedItem.countryId;


this.myremoting.getCupByCountryId(cid);

}



protected function myremoting_resultHandler1(event:ResultEvent):void

{


 pageData =Page( event.result) ;

Alert.show(pageData.currentPage+'c');

Alert.show(pageData.totalPage+'t');

this.ddl.dataProvider=pageData.data;

this.ddl.labelField = "countryName";

this.adg1.dataProvider=pageData.data;



}


protected function myremoting_resultHandler2(event:ResultEvent):void

{


this.adg1.dataProvider=event.result;


}

protected function myremoting_faultHandler(event:FaultEvent):void

{

Alert.show(event.fault.toString());

}


]]>

</fx:Script>

<fx:Declarations>

<s:RemoteObject id="myremoting" showBusyCursor="true" destination="service"

endpoint="http://localhost:8080/sj43/messagebroker/amf"


fault="myremoting_faultHandler(event)"

>

<s:method name="findByPage" result="myremoting_resultHandler1(event)" />

<s:method name="getCupByCountryId" result="myremoting_resultHandler2(event)"/>

</s:RemoteObject>

</fx:Declarations>

<s:Panel width="741" height="427" title="使用HttpService 与后台通信" horizontalCenter="0" verticalCenter="25">

<mx:AdvancedDataGrid x="2" y="9" id="adg1" designViewDataType="flat" width="727" height="274">

<mx:columns>

<mx:AdvancedDataGridColumn headerText="代号" dataField="countryId"/>

<mx:AdvancedDataGridColumn headerText="国家" dataField="countryName"/>

<mx:AdvancedDataGridColumn headerText="金牌数" dataField="goldMedal"/>

<mx:AdvancedDataGridColumn headerText="银牌数" dataField="silverMedal"/>


<mx:AdvancedDataGridColumn headerText="铜牌数" dataField="bronzeMedal"/>


</mx:columns>

</mx:AdvancedDataGrid>

<s:Label x="215" y="361" text="选择国家:" height="23" verticalAlign="middle"/>



<s:Button x="475" y="361" label="显示全部" click="application1_initializeHandler(event as FlexEvent)"/>

<s:DropDownList x="306" y="361" id="ddl" prompt="请选择国家" change="ddl_changeHandler(event)"></s:DropDownList>

<!--以下为分页栏-->

<mx:HBox cornerRadius="0" borderStyle="solid" horizontalAlign="left" verticalAlign="middle" width="722" x="7" y="305" height="27">


<mx:Text fontSize="12" text="{' 第'+pageData.currentPage+'页/共'+pageData.totalPage+'页'+' 共'+pageData.totalClum+'条记录'}"/>


<mx:LinkButton id="lbtnFirst" label="首页"  click="loadData(1)"  enabled="{lbtnPrevious.enabled}" fontSize="12"/>


<mx:LinkButton id="lbtnPrevious" label="上一页" click="loadData(pageData.currentPage-1)"   enabled="{pageData.currentPage!=1?true:false}"  fontSize="12"/>


<mx:LinkButton id="lbtnNext" label="下一页"   click="loadData(pageData.currentPage+1)" enabled="{pageData.totalPage>=(pageData.currentPage+1)?true:false}"  fontSize="12"/>


<mx:LinkButton id="lbtnLast" label="尾页" click="loadData(pageData.totalPage)" enabled="{lbtnNext.enabled}" fontSize="12"/>


<mx:Label   text="每页显示:"/>

<mx:ComboBox id="setPageSize"  width="71" change="loadData(1)">


<mx:dataProvider>

<mx:ArrayList  >



<fx:Object label="5" data="5" />                                                                                                         

<fx:Object label="10" data="10" />

<fx:Object label="20" data="20" />    

</mx:ArrayList>

</mx:dataProvider>

</mx:ComboBox>

<mx:Label   text="条"/>


<mx:NumericStepper id="nsPageNum" stepSize="1" minimum="1" maximum="{pageData.totalPage}"  enabled="{lbtnJump.enabled}" cornerRadius="0" width="54"/>


<mx:LinkButton id="lbtnJump" label="跳转"  click="loadData(nsPageNum.value)" enabled="{pageData.totalPage>1?true:false}"  fontSize="12"/>


</mx:HBox>

</s:Panel>

</s:Application>


(6)运行应用程序,效果如图5.2.4所示。


图5.2.4  分页



















巩固练习

选择题

1.  Flex与外部进行数据通信的方式有()

A.  HTTPService。

B.  WebService。

C.  Remoting。

D.  HttpRequest。

2.  以下关于Flex中Remoting数据通信方式的说法,正确的是()

A.  Remoting使用AMF二进制信息格式化传递数据。

B.  在Flex应用中使用Remoting 技术需要有第三方软件支持。

C.  数据量越大,Remoting方式传输效率越高。

D.  Remoting不支持序列化与反序列化。

3.  以下关于remoting-config.xml文件配置信息的描述 正确的是()。

A.  使用destination节点配置远程调用类的标示。

B.  Source节点代表远程调用类的class文件路径。

C.  一个remoting-config.xml文件只允许配置一个destination节点。

D.  一个remoting-config.xml文件只允许配置多个destination节点。

简答题

(1)什么Remoting数据通信技术?

操作题

开发一个WebService ,用于查询Oracle数据库中的商品信息表,并返回结果。然后通过Flex的WebService组件调用webservice,将获取到的结果显示在表格组件中。​