技术开发 频道

用Java+Flex编写一个简单而强大的RIA

  【IT168 技术】当前对 Web 技术的要求在不断增加。它们必须能够管理用户帐户、上传内容和流媒体格式。这个要求需要 RIA 开发人员探寻这样的技术,即能精简开发流程同时提供广受追捧的功能。开发人员面对的难题就是,如何选择合适的技术集合来提供这些服务。

  Adobe Flex 是一个客户端技术,它为开发人员提供丰富的 API 集合来创建 GUI、绘制图形、播放和流放媒体、连接到 Web 服务。在服务器端,Java 技术提供的功能包括关系型数据库管理系统(RDBM)的连接、服务请求的多线程处理以及随需求增加而进行的非常好的伸缩。将这两种技术结合使用可提供一个满足 RIA 应用程序需求的强大的技术堆栈。

  本文展示如何编写一个简单而强大的 RIA,能使用客户端的 Flex、服务器端 Java 技术以及后端数据库的 MySQL。

  样例应用程序

  样例应用程序(来自下面的 下载 部分)提供一个丰富的 UI,支持通过 Adobe Flash® (SWF) 应用程序创建、读取、更新和删除(CRUD) 联系信息。这个三层的 Web 架构如 图 1 所示,其中客户端由嵌入在一个 Web 页面中的 SWF 文件表示,服务器应用程序在一个 Java servlet 容器(本例中为 Apache Tomcat)内运行,且数据库是 MySQL。这三层共同创建一个功能分布式应用程序。


▲图1 Contacts 应用程序

  对于 Flash 应用程序与 Java servlet 容器之间的通信,Adobe BlazeDS 框架提供对象远程调用 — 即一种允许 Adobe ActionScript™ 对象与 Java 对象相互调用的 PRC 形式。Java 服务器应用程序与关系数据库之间的通信由 Hibernate Object Relational Mapping (ORM) 框架处理。Hibernate 允许将 Java 对象转换为 SQL代码,反之亦然。

  应用程序:服务器层

  第一步是要创建一个 Java 类,它包含存储联系信息所需的信息。样例应用程序包含一个带基本信息的简单模型。Contact 对象所需的属性和数据类型是:

- String emailAddress
- String firstName
- long id
- String lastName
- String phoneNumber
- long serialVersionUID
+ Contact()
+ Contact(String first, String last, String email, String number)
+ String getEmailAddress()
+ String getFirstName()
+ long getId()
+ String getLastName()
+ String getPhoneNumber()
+ void setEmailAddress(String address)
+ void setFirstName(String first)
+ void setId(long newId)
+ void setLastName(String last)
+ void setPhoneNumber(String number)
+ String toString()


  注释业务对象

  Java Contact 类被看作是一个充当业务对象的 POJO(简单 Java 对象),这意味着它代表业务领域特征和行为。Contact 对象内的数据需要存留到数据库中。解决方案就是使用一个 ORM 框架,比如 Hibernate,它在对象与数据库表记录之间的来回映射中执行大量工作。如果使用了 Java Persistence API (JPA) 注释,完成 ORM 仅需要少量代码。 清单 1 显示了带注释的 Java 类 Contact。

  清单 1. Java Contact 类

package bcit.contacts;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name
="contact")
@NamedQueries( {
    @NamedQuery(name
= "contact.findAll", query = "from Contact"),
    @NamedQuery(name
= "contact.getById", query =
        
"select c from Contact c where c.id = :id")
} )
public class Contact {

    
private static final long serialVersionUID = 123456789L;

    
public Contact() {
        firstName
= "N/A";
        lastName
= "N/A";
        emailAddress
= "N/A";
        phoneNumber
= "N/A";
    }

    
public Contact(String first, String last, String email, String number) {
        firstName
= first;
        lastName
= last;
        emailAddress
= email;
        phoneNumber
= number;
    }

    @Id
    @GeneratedValue(strategy
= GenerationType.AUTO)
    @Column(name
= "id", nullable = false, updatable=false)
    
private long id;

    @Column(name
= "lastName", nullable = false, unique = false)
    
private String lastName;

    @Column(name
= "firstName", nullable = false, unique = false)
    
private String firstName;

    @Column(name
= "emailAddress", nullable = false, unique = false)
    
private String emailAddress;

    @Column(name
= "phoneNumber", nullable = false, unique = false)
    
private String phoneNumber;

    
public void setPhoneNumber(String number) { phoneNumber = number; }

    
public String getPhoneNumber() { return phoneNumber; }

    
public String getEmailAddress() { return emailAddress; }

    
public void setEmailAddress(String address) { emailAddress = address; }

    
public String getFirstName() { return firstName; }

    
public void setFirstName(String first) { firstName = first; }

    
public String getLastName() { return lastName; }

    
public void setLastName(String last) { lastName = last; }

    
public long getId() { return id; }

    
public void setId(long newId) { id = newId; }

    @Override
    
public String toString() {
        return id
+ " " + firstName + " " + lastName + " " + emailAddress
            
+ " " + phoneNumber;
    }
}


  类很简单,但对于注释则有很多内容需要解释:

  @Column:将属性标记为数据库内的一个列,可含有列名,不管它是否唯一且是否可为空

  @Entity:将类作为一个实体 bean 声明,表明它是一个要留存的 POJO

  @GeneratedValue:指定生成主键的策略;包括 AUTO、IDENTITY、SEQUENCE 和 TABLE

  @Id:指明属性为每个 Java 对象的唯一标识符(即主键)

  @NamedQueries:列出一组命名查询

  @NamedQuery:将预定义查询声明为一个字符串常量,以供执行时引用。

  @Table:将 Java 类指定为数据库内的一个表

  每次需要留存一个内存中的 Java 对象时,Hibernate 将任何 Java 对象的状态信息转换为一个 SQL 更新。同样地,带结果集的 SQL 语句用于填充 Java 对象。因此,可将所有对象保存为数据库内的记录,且可检索所有记录并将其转换回 Java 对象。

  注释告知 Hibernate 一个类中的哪些内容可以考虑留存。但它们只是类的一部分。

  业务服务:数据库连接

  执行 ORM 时需要有一个服务类来执行对 Hibernate 的调用。清单 2 显示了 ContactsService 类,它充当应用程序服务。

  清单 2. ContactsService 类

public class ContactsService {

    
private static Logger logger = Logger.getLogger(ContactsService.class);

    
private static final String PERSISTENCE_UNIT = "contacts";

    
private static EntityManagerFactory emf = null;

    static {
        logger.info(
"LOADING CONTACTSSERVICE CLASS.");
        emf
= Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
    }

    
public ContactsService() {
        super();
    }

    
public void addContact(Contact c) {
        
if(c == null) {
            return;
        }

        EntityManager em
= emf.createEntityManager();
        logger.info(
"PERSISTENCE ENTITYMANAGER ACQUIRED.");

        logger.info(
"ABOUT TO ADD CONTACT: fName: " + c.getFirstName()
            
+ ", lName: " + c.getLastName() + ", email:" + c.getEmailAddress()
            
+ ", phone: " + c.getPhoneNumber());
        EntityTransaction tx
= em.getTransaction();
        try {
            tx.begin();
            em.merge(c);
            tx.commit();
        } catch (Exception e) {
            logger.error(
"CONTACT APP PERSISTING ERROR: " + e.getMessage());
            tx.rollback();
        } finally {
            logger.info(
"CONTACT APP CLOSING ENTITY MANAGER.");
            em.close();
        }
    }

    
public void editContact(Contact c) {
        logger.info(
"CONTACT TO UPDATE: " + c);
        addContact(c);
    }

    
public void deleteContact(Long id) {
        logger.info(
"ABOUT TO DELETE CONTACT");

        EntityManager em
= emf.createEntityManager();
        logger.info(
"PERSISTENCE ENTITYMANAGER ACQUIRED.");

        Query contactByIdQuery
= em.createNamedQuery("contact.getById");
        contactByIdQuery.setParameter(
"id", id);
        Contact c
= (Contact) contactByIdQuery.getSingleResult();
        EntityTransaction tx
= em.getTransaction();
        try {
            tx.begin();
            em.remove(c);
            tx.commit();

        } catch (Exception e) {
            logger.error(
"CONTACT APP PERSISTING ERROR: " + e.getMessage());
            tx.rollback();
        } finally {
            logger.info(
"CONTACT APP CLOSING ENTITY MANAGER.");
            em.close();
        }
    }

    
public List<Contact> getContacts() {
        logger.info(
"ABOUT TO RETRIEVE CONTACTS");

        EntityManager em
= emf.createEntityManager();
        logger.info(
"PERSISTENCE ENTITYMANAGER ACQUIRED.");

        Query findAllContactsQuery
=
            em.createNamedQuery(
"contact.findAll");
        List
<Contact> contacts = findAllContactsQuery.getResultList();

        
if (contacts != null) {
            logger.debug(
"CONTACT APP RETRIEVED: " + contacts.size()
                
+ " CONTACT(S)");
        }
        return contacts;
    }
}


  每个方法都获取对 EntityManager 的一个引用,它提供对内存缓存的支持。缓存是一个能改进效率的强大特性,因为从一个数据库收发数据是一项很昂贵的操作。您必须确保所创建的每个缓存都服务于数据库,或在不需要时予以回滚。

  在 JPA 中,缓存的既定术语是持久化上下文(persistence context),且它由 EntityManager 类表示。每个持久化上下文管理一组实体,这些实体是标有 @Entity 注释的 Java 对象。EntityManagerFactory 类表示一个 持久化单元(persistence unit),它负责配置到数据存储(例如,关系数据库)的连接、管理实体类型(即给定上下文内需要映射到数据存储的所有类),并最后提供持久化上下文的实例(即一个 EntityManager)。

  试用 DB2 Express 9 数据库服务器免费版DB2 Express-C 被设计为可在几分钟内启动并运行,它易于使用和嵌入,包含自管理功能,且嵌入了 DB2 for Linux®,UNIX® and Windows® 的所有核心功能,比如 pureXML™。DB2 Express-C 像其他 DB2 Express 版本一样提供相同的核心数据服务器基础功能,且提供一个坚实的基础来构建和部署使用 C/C++、Java、.NET®、PHP、Ruby on Rails、Python 和其他编程语言开发的应用程序。

  尽管创建一个持久化上下文的过程很省时,但创建一个持久化单元的过程却很费时。建立到数据存储的连接、查找标注为实体的所有类、配置持久化逻辑以将这些类绑定到数据存储中的实体,整个过程不可能快速完成。因此,您需要在应用程序启动时创建一个 EntityManagerFactory 实例。对于持久化上下文,要务必确保在销毁掉一个 EntityManager 之后再创建另一个。另一个要遵循的重要规则就是 entitymanager-per-request 模式。该模式将数据库调用(例如,请求和更新)组合起来,这样就可以将它们一次性发送出去。这样做可以确保充分利用 JPA 的缓存机制。

  下一个需求就是客户端。

  应用程序:客户端层

  Flex 框架允许您创建可在 Adobe Flash Player 中播放的应用程序。Flex 包括:

  称为 MXML 的一个声明性 XML UI 语言

  ActionScript 编程语言

  用于创建 UI、Web 连接和许多其他特性的运行时库

  用于将应用程序编译为 SWF 文件的开发工具

 

  本文引用的客户端应用程序使用 Flex 第 4 版。在探讨客户端应用程序之前,要了解如何创建 Flex 应用程序,以及它们如何在 Flash Player 中作为可执行程序存在,这很重要。

  首先,您可以使用 MXML 标记和 ActionScript 代码创建应用程序。常用的工作流是使用 MXML 格式创建 GUI 的主要部分(呈现),然后使用 ActionScript 代码执行事件处理和业务逻辑。由于 MXML 和 ActionScript 都是基于文本的,创建 Flash 应用程序只需要一个标准文本编辑器和 Flex SDK。

  其次,编写完 Flex 应用程序之后,使用 MXML 编译器编译代码。然后 MXML 编译器创建可在 Web 浏览器内运行(通过 Flash Player 浏览器插件)的 SWF 文件。

  最后,Flash 应用程序在使用时间轴范例的 ActionScript Virtual Machine 2 (AVM2) 中运行。该范例将执行动作分成帧 — 就像电影一样。您在编译时指定 Flash 应用程序中的每秒帧数。此外,Flash Player 将执行动作分成以下已排序的任务:

  Flash Player 事件,比如计时器和鼠标事件

  用户代码

  预渲染逻辑,其中 Flash Player 试图确定是否因数据值变更而更新 GUI

  与数据值变更相关的用户代码

  Flash Player 渲染

  如果要渲染的每秒帧数很少,那么就可以执行大部分用户代码。但是,如果帧频很高(例如,每秒 60 帧),Flash Player 就不太可能执行多数用户代码,因为用户代码执行的时间可能比给定时间更长。在为 Flash Player 编写代码时,记住这一点很重要。

  MXML

  MXML 是一个强大的声明性 XML 格式,有助于:

  因 XML 格式的声明性质而最大限度地降低构建 GUI 所需的代码量

  通过明确分离表示逻辑和交互逻辑降低 GUI 代码的复杂度

  在进行软件开发时推进设计模式的使用

  清单 3 显示了 MXML Application 类。

  清单 3. ContactsApp 类

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  xmlns:contact
="bcit.contacts.*" creationComplete="initPage();"
  layout
="vertical" frameRate="30" pageTitle="Contacts Example"
  horizontalAlign
="center" verticalAlign="middle"
  backgroundColor
="#A9C0E7">


  
<mx:Style>
    .mainBoxStyle {
      borderStyle: solid;
      paddingTop: 5px;
      paddingBottom: 5px;
      paddingLeft: 5px;
      paddingRight: 5px;
    }

    .textMessages {
      fontWeight: bold;
    }
  
</mx:Style>


  
<mx:RemoteObject id="remotingService" showBusyCursor="false"
    destination
="contacts" fault="handleFault(event);"
    result
="handleResult(event);"/>

  
<mx:Script>
    
<![CDATA[

        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;
        import mx.collections.ArrayCollection;
        import bcit.contacts.dto.Contact;

        [Bindable]
        
private var contacts:ArrayCollection = new ArrayCollection();

        
// For more on the Bindable metadata tag, see the devguide_flex3.pdf
        
// document, page 1249 (1257 in PDF page numbering)
        [Bindable]
        
private var message:String = "Status: Ready";

        
private var contact:Contact;

        
public function setControlBarValid(valid:Boolean):void {
            
if(valid) {
                
// if the selected item is -1, then no item is selected but at
                
// the same time the fields are valid which means the user chose
                
// to add a contact, not update one
                
if(contactsDataGrid.selectedIndex == -1) {
                    createButton.enabled
= valid;
                }
else {
                    editButton.enabled
= valid;
                }
              }
else {
                  
// else nothing is valid
                  createButton.enabled
= false;
                  editButton.enabled
= false;
            }
        }

        
private function initPage():void {
            editContactForm.setApp(this);
            contact
= new Contact();
            getAllContacts();
            resetPage();
        }

        
private function createContact():void {
            contact
= editContactForm.getContact();
            remotingService.addContact(contact);
            message
= "Status: Contact Added";
            getAllContacts();
        }

        
private function editContact():void {
            var id:Number
= contact.id;
            contact
= editContactForm.getContact();
            contact.id
= id;
            remotingService.editContact(contact);
            message
= "Status: Contact Edited";
            getAllContacts();
        }

        
private function deleteContact():void {
            
if(contactsDataGrid.selectedItem != null) {
                var c:Contact
= contactsDataGrid.selectedItem as Contact;
                
// no sense in sending the whole contact - just send the id
                
// to cut down on bandwidth
                remotingService.deleteContact(c.id);
                message
= "Status: Contact Deleted";
            }
            getAllContacts();
        }

        
private function getAllContacts():void {
            loadButton.enabled
= false;
            remotingService.getContacts();
            loadButton.enabled
= true;
            resetPage();
        }

        
private function populateFormWithContact():void {
            contact
= contactsDataGrid.selectedItem as Contact;
            editContactForm.setContact(contact);
            editButton.enabled
= true;
            deleteButton.enabled
= true;
        }

        
private function resetPage():void {
            editContactForm.clearForm();
            contact
= new Contact();
            createButton.enabled
= false;
            editButton.enabled
= false;
            deleteButton.enabled
= false;
            contactsDataGrid.selectedIndex
= -1;
        }

        
private function handleFault(e:FaultEvent):void {
            message
= "Status: Error"
                
+ "\nFault code: " + e.fault.faultCode
                
+ "\nFault detail: " + e.fault.faultDetail
                
+ "\nFault string: " + e.fault.faultString;
        }

        
private function handleResult(e:ResultEvent):void {
            
// can get the results by accessing e.result property
            
//mx.controls.Alert.show(e.toString());
            contacts
= e.result as ArrayCollection;
            var number:
int = contacts.length;
            
//if(number == 1) {
            
//    message = "Status: Retrieved 1 contact";
            
//} else {
            
//    message = "Status: Retrieved " + contacts.length + " contacts";
            
//}
        }

    ]]
>
  
</mx:Script>

  
<mx:VBox styleName="mainBoxStyle">

    
<mx:Text id="titleText" text="Single click to select a contact"/>

    
<contact:ContactsDataGrid id="contactsDataGrid" dataProvider="{contacts}"
      itemClick
="populateFormWithContact();"
      doubleClick
="populateFormWithContact();"/>

    
<contact:EditContactForm id="editContactForm"/>

    
<mx:ControlBar horizontalAlign="center">
      
<mx:Button label="List" id="loadButton" click="getAllContacts()"
        toolTip
="Retrieve contacts from the server"/>
      
<mx:Button label="Add" id="createButton" click="createContact()"
        toolTip
="Create a new contact"/>
      
<mx:Button label="Update" id="editButton" click="editContact()"
        toolTip
="Edit a selected contact"/>
      
<mx:Button label="Delete" id="deleteButton" click="deleteContact()"
        toolTip
="Delete a selected contact"/>
      
<mx:Button label="Clear Form" id="clearButton" click="resetPage()"
        toolTip
="Clear the form"/>
    
</mx:ControlBar>

    
<mx:TextArea text="{message}" styleName="textMessages" wordWrap="true"
      verticalScrollPolicy
="auto" horizontalScrollPolicy="off" editable="false"
      width
="100%"/>

  
</mx:VBox>

</mx:Application>


  关于清单 3,这里还有几点说明:

  一个 MXML 文档的根元素是 Application 类的一个子类。

  mx:Style 元素允许 CSS 属性定义 UI 组件的本地样式。样式设计通过使用本地样式定义(见 清单 3)、对外部样式表的引用、组件内的内联样式以及 ActionScript 中的 setStyle 方法完成。

  RemoteObject 类表示与服务器执行远程操作的一个 HTTP 服务对象。

  mx:Script 元素包括 CDATA 区域中的 ActionScript 代码块。

  有一个布局(即 VBox 类)。

  每次在应用程序中声明一个 UI 组件(例如,TextArea)时,就产生一个实例变量,稍后可使用组件的 id 属性在应用程序内引用该变量。

  数据绑定通过大括号执行(例如, 将 TextArea 元素的 text 属性绑定到 ActionScript message 实例变量)。

  ActionScript

  MXML 定义 GUI,而 ActionScript 提供用于处理事件、绑定数据(通过 [Bindable] 元数据标记)的行为,以及调用远程服务的能力。在 清单 3 中,createContact、editContact、deleteContact 和 getAllContacts 方法都在服务器端调用远程方法。调用远程方法时,ActionScript 就有机会通过声明回调函数处理结果和任何错误了。在 清单 3 中,handleResult 函数将结果作为一个 Object 接收,并将其投射到一个 ArrayCollection。BlazeDS 将 List 转化为客户端上的一个 ArrayCollection。

  清单 4 展示了 ActionScript 类 Contact,创建该类的目的在于表示 Flash 端的联系对象。

  清单 4. ActionScript Contact 类

package bcit.contacts.dto {

[RemoteClass(alias
="bcit.contacts.Contact")]
public class Contact {

    
public function Contact() { id = -1; }

    
public var id:Number;
    
public var lastName:String;
    
public var firstName:String;
    
public var emailAddress:String;
    
public var phoneNumber:String;

    
public function toString():String {
        return id
+ ", " + firstName + " " + lastName + " " + emailAddress
            
+ " " + phoneNumber;
    }
}
}


  这些 ActionScript 对象被发送到服务器端,BlazeDS 在此发挥其魔力并将 ActionScript 对象转化为 Java 对象。ActionScript Contact 类被看作一个 Data Transfer Object (DTO)。

  配置应用程序

  应用程序也依赖于为服务器声明设置规范的配置文件。这个应用程序内的两个主要配置对象是 Hibernate 和 BlazeDS。

  配置 Hibernate

  您可以使用标准 JPA 配置文件 persistence.xml 配置 Hibernate,如 清单 5 所示。

  清单 5. persistence.xml 配置文件的一个子集

<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation
="http://java.sun.com/xml/ns/persistence
  http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="contacts" transaction-type="RESOURCE_LOCAL">
    
<properties>
      
<property name="hibernate.dialect"
        value
="org.hibernate.dialect.MySQLDialect" />
      
<property name="hibernate.default_schema" value="contacts" />
      
<property name="hibernate.connection.driver_class"
        value
="com.mysql.jdbc.Driver" />
      
<property name="hibernate.connection.url"
        value
="jdbc:mysql://localhost:3306/contacts" />
      
<property name="hibernate.archive.autodetection" value="class, hbm"/>
      
<property name="hibernate.connection.username" value="root"/>
      
<property name="hibernate.connection.password" value="root"/>
    
</properties>
  
</persistence-unit>
</persistence>


  persistence.xml 文件必须位于 Web 应用程序的 WEB-INF/classes/META-INF 文件夹中,Hibernate 才能读取到它。具备了这个条件之后,Hibernate 需要以下信息:

  数据库方言(即它与哪个数据库对话,因为很多数据库都有略微不同的 SQL 方言)

  经由默认架构的表空间

  用于连接数据库的数据库驱动器

  数据库 URL

  自动检测功能应检测什么(例如,注释类、Hibernate 映射 XML 文件等)

  用户名和密码

  其他信息也有助于提高 Hibernate 的性能,但不是必需的。

  配置 BlazeDS

  BlazeDS 有 4 个配置文件:

  messaging-config.xml:定义发布-订阅消息传送信息

  proxy-config.xml:为 HTTP 和 Web 服务提供代理服务信息

  remoting-config.xml:定义用于远程服务的信息,比如本文应用程序的配置文件

  services-config.xml:引用其他配置文件且提供安全约束、通道和日志记录的优异配置文件

  清单 6 展示了 services-config.xml 文件。注意,对于本文应用程序,只有 remoting-config.xml 文件是相关的,因为应用程序仅使用了 BlazeDS 远程服务。

  清单 6. services-config.xml 配置文件的子集

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
  
<services>
    
<service-include file-path="remoting-config.xml" />
    
<service-include file-path="messaging-config.xml" />
    
<service-include file-path="proxy-config.xml" />
    
<default-channels>
      
<channel ref="contacts-amf"/>
    
</default-channels>
  
</services>

  
<channels>
    
<channel-definition id="contacts-amf" class="mx.messaging.channels.AMFChannel">
      
<endpoint url="http://localhost:8080/contacts/messagebroker/amf"
        class
="flex.messaging.endpoints.AMFEndpoint"/>
      
<properties>
        
<polling-enabled>false</polling-enabled>
      
</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>

</services-config>


  services-config.xml 配置文件引用其他配置文件(如果存在)、配置 BlazeDS 日志记录并建立任何通道。一个通道 是对协议的一个抽象,供客户端与服务器通信时使用。本文应用程序使用没有轮询的标准 AMF 协议。轮询 是指客户端持续与服务器通信,以确保连接始终成立 — 不过在本应用程序中不需要。

  通道端点指定服务器 URL。该端点是编译项目所必需的;客户端 Flash 应用程序将其作为一个硬编码值使用,因此它知道要连接到哪个服务器上。您实际上可以在 MXML 或 ActionScript 中定义端点 URL。

  最后,remoting-config.xml 配置文件(如 清单 7 所示)指定适配器类,用于处理远程操作和响应远程调用的实际类。(本例中是将 bcit.contacts.ContactsService 类作为对远程请求的响应者提供。)

  清单 7. 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" default="true"
      class
="flex.messaging.services.remoting.adapters.JavaAdapter"/>
  
</adapters>

  
<default-channels>
    
<channel ref="contacts-amf"/>
  
</default-channels>

  
<destination id="contacts">
    
<properties>
      
<source>bcit.contacts.ContactsService</source>
      
<!--<scope>application</scope>-->
    
</properties>
  
</destination>

</service>


  结束语

  本文向您展示了如何编写一个运行在 Tomcat 内,并响应联系信息请求的 Java 服务器端 Web 应用程序。您学习了如何同时使用 MXML 和 ActionScript 编写一个 Flex 应用程序,以创建一个客户端 Flash 应用程序。MySQL 充当数据存储,而 Hibernate — 一个 ORM 框架 — 用于将 Java 对象转换成能查询和更新 MySQL 数据库的 SQL 语句。最后,BlazeDS 框架允许 Flash 应用程序进行远程过程调用并在 Java 服务器端 Web 应用程序执行远程调用。

0
相关文章