J2EE Web开发使用Ajax技术的

核心所在

 

 

 

作者:Henry Yu

日期:2007-11-20

邮件:yuhaodong@gmail.com

 

 

 

 


目录

1.     前言.... 1-3

2.     开发模式化.... 2-3

2.1    Javascript客户端组件... 2-4

2.1.1    Request 2-4

2.1.2    Response. 2-5

2.2    服务端组件... 2-6

2.2.1    ICommonAjax子控制接口... 2-6

2.2.2    AjaxRequest请求参数对象... 2-7

2.2.3    AjaxResponse结果响应对象... 2-7

2.3    示例... 2-8

3.     数据格式透明化.... 3-11

3.1    基本数据类型... 3-11

3.2    自定义数据对象... 3-13

3.3    数据列表集... 3-14

3.4    复杂数据对象... 3-16

4.     数据解析与展示.... 4-16

4.1    基础DOM编程... 4-17

4.1.1        示例1:(利用DOM操作页面元素,固定填充)... 4-17

4.1.2        示例2:(利用DOM动态生成表格)... 4-19

4.2    jQuery编程... 4-21

4.2.1        示例1:(利用jQuery,固定填充)... 4-21

4.2.2        示例2:(利用jQuery,动态生成表格)... 4-21

4.3    高级UI 4-22

4.3.1        示例1:(<form>表单元素)... 4-23

4.3.2        示例2:(<table>表格)... 4-23

4.3.3        示例3:(tree树)... 4-24

5.     其它话题.... 5-24

5.1    同步与异步... 5-24

5.2    AOP横切编程... 5-26

5.3    ServerPush技术... 5-29

6.     结束语.... 6-29

 

1.       前言

最近,经常收到网友的Email,问现有的Beetle Web框架对Ajax开发支持的情况。Ajax是现在不能回避的话题了。Beetle Web框架提供了一个Ajax控制器功能模块,对Ajax提供了强而有力的支持。

事实上,纯粹讨论Ajax技术本身是没有多大意思的,因为其核心是XMLHttpRequest组件,它的使用是十分简单的。我们关心是Ajax技术的引入对现有传统J2EE Web开发方式的影响和挑战,进而论述Beetle Web框架封装Ajax的理念和机制,从而最终了解J2EE Web开发使用Ajax技术的核心所在。

先回顾一下Ajax技术引入对传统Web开发挑战:

n           开发方式模糊

传统的MVC开发方式可以说是深入民心了,Ajax引入使得传统以Html内嵌javajsp视图“消失了”,代替的是发起请求的页面本身,而此时视图展现内容的方式,主要是Javascript操作Html。这种转变打破了传统方式易于开发、维护等的优雅特性,使得开发模糊化,复杂化。特别地,当前市场上提供了形形色色的所谓Ajax框架,它们开发模型和理念也是千变万化,各不相同。这进一步模糊了Web开发模式,使得Web开发更加复杂化。

n           系统异构性

传统模型是纯服务端编程,ModelViewController都是由java单一语言开发,不存在语言差异性带来的数据对象序列化问题;Ajax的引入使得View必须由Javascript语言开发,使原来纯粹服务端编程模型演变成“客户端--服务端”的异构编程模型。JavaJavascript语言上差异性所带来的对象序列化、数据转换等问题,是这个异构编程模型的最大挑战。同时,对于Web开发人员来说,除了java之外,意味着Javascript语言和DOMDocument Object Model)文档对象模型是必须掌握的基本技能。

n           数据传输交换复杂性

异构性导致数据传输交换的复杂性。针对传输的性能、数据的格式、序列化的深度、数据的解析与展现等诸多复杂问题必须要有一套满足开发需求的良好解决方案。

清楚所面临的问题,我们就不难发现J2EE Web开发使用Ajax技术的核心所在:对开发过程进行模式化、框架化。降低开发难度,解决效率、维护和扩展等问题;解决数据传输效率,对数据交换格式透明化处理,从而消除由于系统异构性带来Javascriptjava数据交互问题;返回到客户端页面的数据,最终需要呈现给用户,显然,解决返回数据的解析与显示至关重要。

Beetle Web框架就是基于上面目标对Ajax技术进行封装的。下面,就从上述几个核心问题的角度来阐述一下Beetle Web框架在Ajax领域的开发理念和应用实现。

 

2.       开发模式化

开发模式化的根本是提供一个开发框架来约束和规范化程序员编码的行为,达到代码可控制、可维护、可扩展的目的。由于开发人员对类似StuctsBeetle Web等传统MVC设计模式、Servlet很熟悉,为了让开发人员对Ajax技术能够平移过渡,Beetle Web框架也采取MVC设计模式对Ajax进行封装,提供了完整请求驱动框架。其框架结构图如下:

1 Beetle Web Ajax框架结构图

浏览器采取Javascript调用XMLHttpRequestMozillaMicrosoft各有实现)后台底层Http请求组件与远程的J2EE Web Servlet容器通信:发出的请求交给AjaxMainServlet服务程序去负责统一处理,它通过AjaxConfig配置类读取部署在WebController.xml文件中的Ajax子控制器配置信息。然后,根据浏览器提交请求的子控制器名称来匹配其对应的处理程序,完成请求逻辑并把结果数据返回给浏览器客户端。最后,浏览器再利用Javascript来解析并展现这些结果数据。

所以,Beetle Web Ajax框架开发分为:Javascript客户端组件和Java服务器组件两大部分。下面各自加以说明。

2.1        Javascript客户端组件

Javascript客户端主要有:Request请求和Response响应两个组件,设计思想来源于J2EE Servlet容器的HttpServletRequestHttpServletResponse,并相应地与Java服务端AjaxRequestAjaxResponse对应。这两个对象定义在jsonrpcEx.js库中。

2.1.1              Request

Request封装了XMLHttpRequest组件负责页面参数的输入、服务端处理请求子控制器程序的指定和页面请求的发起。其主要功能方法说明如下:

方法与属性

功能说明

Request ()

构造方法

put(name,value)

页面参数输入方法。

参数:

name唯一参数名,服务端需要根据此名称获取参数值;

value参数值

结果:

无返回

setControllerName(controllerName)

设置服务端处理此请求控制器的名称,此控制器在需要在WebController.xml文件配置。

参数:

controllerName处理请求的子控制器名称。

结果:

无返回

disableFrontAction()

禁止此请求参与全局前置横切调用[1]

结果:

无返回

disableBackAction()

禁止此请求参与全局后置横切调用[2]

结果:

无返回

synchroExecute()

同步提交请求给服务端执行,并返回请求执行结果。

参数:

结果:

Response,响应对象,包含结果和执行状态标记

asynchroExecute(asyncResultHandle)

异步提交请求给服务器执行。通过回调函数处理结果。

参数:

asyncResultHandle(response)为结果处理的回调函数,其输入参数为一个Response对象。

结果:

无返回

disableDebugInfo()

禁止页面出现错误异常信息弹出窗口。

[1] [2] Beetle Ajax 框架为了支持AOP横切编程,在控制器执行过程中设置了前、后两个回调函数。关于AOP编程说明请参考后面的其它话题章节。

 

2.1.2              Response

Response相应对象负责对请求返回结果数据的解析和抽取,并提供请求在服务器端执行情况的状态查询。其主要功能方法说明如下:

方法与属性

功能说明

Response (mapValue)

构造方法,由Request对象执行请求后自动创建,一般无须手工创建。

returnFlag

请求在服务器端执行状态标记属性,一般地,其值为负数,则代表服务器执行异常;其值大于等于0,则代表服务器执行成功。此标记值,在编写服务端控制器代码时人为定义。(如果不显式定义,则成功为0,失败为-1

returnMsg

请求在服务器端执行状态信息描述属性。

getValueByName(name)

根据名称获取其对应的Javascript值对象。

参数:

name结果数据项的名称,在服务端返回时候设置。

结果:

相应的Javascript对象

getValueAsArrayListByName(name)

根据名称获取其对应的Javascript值对象列表。

参数:

name结果数据项的名称,在服务端返回时候设置。

结果:

ArrayList[1]对象,包含所有返回的数据值对象。

getValueAsDateByName(name)

根据名称以Date对象形式返回值

参数:

name结果数据项的名称,在服务端返回时候设置。

结果:Javascript内置的Date对象

isBreak()

流程是否中断,详细参考AOP编程章节

[1] ArrayList是为了方便解析结果集也参照java.util.ArrayList类而建立的Javascript对象,其属性和方法与java.util.ArrayList一致。定义在jsonrpcEx.js库中。

 

2.2        服务端组件

服务端采取一个子控制器来处理浏览器发送过来的请求,这个子控制器编程入口是ICommonAjax接口;Javascript提交请求参数由AjaxRequest对象负责解析和获取;执行结果则由AjaxResponse对象负责组织返回。这3个对象的类图如下:

2 服务端组件类图

下面分别阐述一下它们的功能作用。

2.2.1              ICommonAjax子控制接口

ICommonAjax是请求处理程序的入口所有的Ajax子控制器都必须实现此接口其定义如下

public interface ICommonAjax {

  /**

   * ajax控制逻辑执行方法AjaxMainServlet会根据请求的名称来找到此接口的实现类

   * 并执行此方法完成任务

   *

   * @param AjaxRequest 浏览器客户端提交的参数对象,参照传统的Http Request设计。提供一些方法便于获取参数。

   * @return AjaxResponse 返回一个AjaxResponse结果响应的数据对象,其数据格式为json。因为ajax是后台刷新,也就是说返回的视图就是它自己本身。

   * @throws ControllerException

   */

  AjaxResponse perform(AjaxRequest request) throws ControllerException;

}

可见,其执行过程很明显:在perform方法体内,首先从输入参数AjaxRequest对象中获取页面提交上来的数据,然后编程处理请求的逻辑,最后生成结果的数据放在AjaxResponse对象中,并返回。

2.2.2              AjaxRequest请求参数对象

从图2可以知道AjaxRequest提供了一系列的getParameterAsXXX来获取页面提交过来的参数并转换成其所对应的java类型。事实上,AjaxRequest是参照传统的HttpServletRequest设计的,所以在此对象中还提供了HttpServletRequest对象在访问SessionCookie和此请求本身属性的相关操作方法。

 

2.2.3              AjaxResponse结果响应对象

AjaxResponse继承自HashMap也就是说它利用一个Map来保存服务端返回给客户端的数据。从图2可知,它还提供了3个属性和setValue方法,详细说明如下:

方法与属性

功能说明

AjaxResponse()

构造方法。

returnFlag

请求在服务器端执行状态标记属性,一般地,其值为负数,则代表服务器执行异常;其值大于等于0,则代表服务器执行成功。此标记值,在编写服务端控制器代码时人为定义。(如果不显式定义,则成功为0,失败为-1

returnMsg

请求在服务器端执行状态信息描述属性。

breakFlag

流程是否中断属性,详细参考AOP编程章节

setValue(String name, Object value)

根据名称,设置返回值。

参数:

name值名称,为字符串类型

value返回值,为Object类型

 

2.3        示例

Beetle Web Ajax框架中,客户端组件与服务端组件一起协调完成一个Ajax请求处理过程,下面具体举例说明一下:

登录例子:页面输入用户名和密码,后台调用一个ajax子控制器验证,验证通过返回此用户信息,失败则返回错误原因。采取ajax技术,页面不刷新。

3 登录界面

4 成功界面

5 失败界面

客户端Javascript代码:

var req=new Request();//创建一个请求对象

req.setControllerName("LoginAjaxController.ajax");//指定后台处理控制器

req.put("username",f2.username.value);//设置页面输入参数

req.put("pwd",f2.pwd.value);

var r=req.synchroExecute();//提交请求并返回Response结果对象(同步执行)

if(r.returnFlag==0){//通过执行标记查询后台执行状态,决定程序流程

  var userInfo=r.getValueByName("UserInfo");//按名称获取响应结果数据对象

  var username=userInfo.username;

  var pwd=userInfo.pwd;

  var sex=userInfo.sex;

  var email=userInfo.email;

  var registTime=new Date(userInfo.registTime.time);

  alert(r.returnMsg+'\n-->用户信息<--\n姓名:'+username+'\n性别:'+sex+'\n邮件:'+email+'\n注册时间:'+registTime.toString());//打印结果信息

}else{

  alert(r.returnMsg);

}

服务端java代码:

package ajaxdemo.controller;

import ajaxdemo.vo.User;

import com.beetle.framework.web.controller.ajax.ControllerException;

import com.beetle.framework.web.controller.ajax.AjaxRequest;

import com.beetle.framework.web.controller.ajax.AjaxResponse;

import com.beetle.framework.web.controller.ajax.ICommonAjax;

public class LoginController implements ICommonAjax {//实现Ajax子控制接口

    public AjaxResponse perform(AjaxRequest req) throws ControllerException {

       AjaxResponse res = new AjaxResponse();//创建响应对象以便返回

       String username = req.getParameter("username");//通过req获取请求参数

       String pwd = req.getParameter("pwd");

        //假设数据已在Session里面(Model数据由具体逻辑产生)

       User user = (User) req.getDataFromSession("UserInfo");

       if (user.getUsername().equals(username)

           && user.getPwd().equals(pwd)) {

           res.setReturnFlag(0);//设置执行标记

           res.setReturnMsg("登录成功!");//描述此标记

           res.setValue("UserInfo", user);//设置返回结果的用户对象

       } else {

           res.setReturnFlag(-1);

           res.setReturnMsg("登录失败,请检查你的密码!");

       }

       return res;//返回响应对象

    }

}

/config/ WebController.xml文件中装配上面的LoginController子控制器:(主要是让框架根据页面提交上来的控制器名称找到其对应的处理子控制器)

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

<mappings>

  <controllers>

     <ajax>

       <aItem name="LoginAjaxController.ajax" class="ajaxdemo.controller.LoginController"/>

    </ajax>

  </controllers>

</mappings>

上面整个ajax请求访问过程的各组件的调用关系如下:

6 UML顺序图

首先,浏览器html页面引入jsonrpcEx.js库,在登录按钮事件内创建一个Request请求对象,调用其setControllerName()方法指定服务器处理此请求的子控制器名称,调用put()方法输入请求参数,调用synchroExecute()方法提交请求。

其次,AjaxMainServlet主控器接收到通过http协议提交上来的请求,根据控制器名称找到其对应的LoginController实现类,并实例化它,调用其perform()方法执行处理请求的逻辑,并返回AjaxResponse结果对象给主控器,主控制将此结果进行序列化,以MapValue数据形式返回到浏览器的javascript客户端。

最后,javacirptrequest对象把传回的数据封装成一个Response对象交回给html页面,再通过Response对象提供的getValueByName方法读取返回的数据解析显示在页面视图上。

 

*当前不少Ajax框架关注的是技术上灵活性,例如:著名的DWR,它可以将服务器端 Java 对象的方法直接暴露到页面端Javascript代码,这样好处是消除了一般Ajax请求--响应操作过程。虽然这样封装很“漂亮”,但并不是完全透明的,还是需要在dwr.xml配置文件中进行java对象的指定和装配;另外,过度的序列化匹配与远程调用对性能来说是一个很大牺牲;然而,DWR最大的问题还是在开发模式上面,从本质上来看,DWR客户端与服务端的代码之间还是形成了紧密的耦合,DWR也没有对开发方式进行界定,基本上任何的Java对象都可以随心所欲的暴露给客户端,使得代码无法控制和统一调优,更难以维护。

相对DWR,显然,Beetle Web Ajax来得轻量、高效和简单,合理地解耦了页面和服务端代码之间的关系,也不存在开发模式的问题。达到了框架控制开发方式和程序代码的目标。

 

3.       数据格式透明化

Ajax词语中“x”指的是xml,就是客户端与服务端数据传输中采取的常见数据格式。然而,xml用来描述数据的格式是十分臃肿,具有传输性能低、解析效率低等缺点。Beetle Ajax采取json文本格式来代替xml作为客户端与服务端数据传输的格式。jsonxml比较有以下优势:

n           对数据描述比xml来得简单、轻量。描述对象越复杂、数量越多,效果越明显。这意味着对于同一数据对象,采取jsonxml在网络传输的时候承载的数据量更小、速度更快。

n           json天生就是Javascript对象文字符号的子集,Javascript解析json比解析xml来简单得多,速度快得多。因而,浏览器解析响应速度就更快、更友好。

解决了数据传输格式和效率的问题,接下来考虑的是数据格式的透明化处理。也就是说,在整个ajax开发编程交互的过程中,开发人员无须关心数据传输的格式,也无须关心其在Javascriptjava两种异构语言之间的转换机制。数据在页面端呈现给开发人员的是Javascript数据对象,在服务端呈现给开发人员的是java值对象。

对此,Beetle Ajax作了有效的封装,支持的数据类型有:基本数据类型、自定义的数据对象、对象列表、对象数组等。详细说明如下:

3.1        基本数据类型

Beetle Ajax框架支持Javascriptjava基本数据类型的互换传输,这些类型包括:

Javascript基本类型

对应的java基本类型

Boolean

Boolean,通过AjaxRequest提供的 getParameterAsBoolean()方法进行转换获取。

Number

FloatIntegerDoubleLongdoublelongintfloat,根据具体提交上来的数值,通过AjaxRequest提供的一系列getParameterAsXXX()方法获取。

Date

java.sql.Timestamp,通过AjaxRequest提供的getParameterAsTimestamp()方法进行转换获取。

String

String,通过AjaxRequest提供的getParameter ()方法进行转换获取。

Array

Object[],通过AjaxRequest提供的getParameterAsArray ()方法进行转换获取。

示例如下:

7 基本数据类型演示

页面Javascript代码:

function baseShow(){

    var req=new Request();

    req.setControllerName("DataTypeDemoAjaxController.ajax");

    var bl=new Boolean(true);

    var nb=new Number(1976.224);

    var mybirthday=new Date(1976,1,24);

    var str="Hello world!";

    var ary=new Array(2);

    ary[0]="Henry Yu";

    ary[1]=31;

    req.put("bl",bl);

    req.put("nb",nb);

    req.put("str",str);

    req.put("mybirthday",mybirthday);

    req.put("ary",ary);

    req.put("flag",0);

    var r=req.synchroExecute();

    if(r.returnFlag==0){

      var echo=r.getValueByName("echo");

      alert(echo);

    }else{

      alert(r.returnMsg);

    }

}

服务端java代码:

package ajaxdemo.controller;

import java.util.List;

import ajaxdemo.vo.User;

import com.beetle.framework.web.controller.ajax.ControllerException;

import com.beetle.framework.web.controller.ajax.AjaxRequest;

import com.beetle.framework.web.controller.ajax.AjaxResponse;

import com.beetle.framework.web.controller.ajax.ICommonAjax;

public class DataTypeDemoController implements ICommonAjax {

    public AjaxResponse perform(AjaxRequest req) throws ControllerException {

       AjaxResponse res = new AjaxResponse();

       int flag = req.getParameterAsInt("flag");

       Boolean bl = req.getParameterAsBoolean("bl");

       Double nb = req.getParameterAsDouble("nb");

         java.sql.Timestamp mybirthday = req.getParameterAsTimestamp("mybirthday");

       String str = req.getParameter("str");

       Object []ary = req.getParameterAsArray("ary");

       StringBuffer sb = new StringBuffer();

       sb.append("echo:\n");

       sb.append("Boolean--" + bl + "\n");

       sb.append("Number--" + nb + "\n");

       sb.append("Date--" + mybirthday.toString() + "\n");

       sb.append("String--" + str + "\n");

       sb.append("Array--\n");

       for (int i = 0; i < ary.length; i++) {

           sb.append(ary[i].toString());

           sb.append("\n");

       }

       res.setValue("echo", sb.toString());

       System.out.println(sb.toString());

       return res;

    }

}

页面请求创建和子控制器编程与前面第2小节的编程模式是一致的,这里就不再说明了。

3.2        自定义数据对象

Beetle Ajax框架支持Javascript只定义数据对象与java标准POJO值对象之间的无缝互换。在服务端通过AjaxRequest提供的getParameterAsObject方法自动匹配。

示例如下:

8自定义数据对象示例

页面Javascript代码:

//定义userinfo对象

function userInfo(username,pwd,sex,email){

  this.username=username;

  this.pwd=pwd;

  this.sex=sex;

  this.email=email;

  this.javaClass="ajaxdemo.vo.User";//指定服务端所对应的java值对象(可选)

}

function selfDefShow(){

    var user=new userInfo("Henry Yu","888888","M","henryyu@163.com");

    var req=new Request();

    req.setControllerName("DataTypeDemoAjaxController.ajax");

    req.put("flag",1);

    req.put("userInfo",user);//放入req对象以便提交到服务端

    var r=req.synchroExecute();

    if(r.returnFlag==0){

      var user=r.getValueByName("user");//获取服务端传回值对象

      var username=user.username;//获取对象属性的值

      var pwd=user.pwd;

      var sex=user.sex;

      var email=user.email;

      alert('-->用户信息<--\n姓名:'+username+'\n性别:'+sex+'\n邮件:'+email);

    }else{

      alert(r.returnMsg);

    }

}

服务端java代码:

package ajaxdemo.controller;

import java.util.List;

import ajaxdemo.vo.User;

import com.beetle.framework.web.controller.ajax.ControllerException;

import com.beetle.framework.web.controller.ajax.AjaxRequest;

import com.beetle.framework.web.controller.ajax.AjaxResponse;

import com.beetle.framework.web.controller.ajax.ICommonAjax;

public class DataTypeDemoController implements ICommonAjax {

    public AjaxResponse perform(AjaxRequest req) throws ControllerException {

       AjaxResponse res = new AjaxResponse();

       int flag = req.getParameterAsInt("flag");

       User user=(User)req.getParameterAsObject("userInfo");[1]

       StringBuffer sb = new StringBuffer();

       sb.append("echo:\n");

       sb.append("username:"+user.getUsername()+"\n");

       sb.append("pwd:"+user.getPwd()+"\n");

       sb.append("sex:"+user.getSex()+"\n");

       sb.append("email:"+user.getEmail()+"\n");

       System.out.println(sb.toString());

       res.setValue("user", user);//传回给Javascript客户端

       return res;

    }

}

[1]java值对象能够与Javascript定义的数据对象自动匹配的前提条件是:两个对象的属性名称要保证一致(大小写不敏感)

3.3        数据列表集

客户端与服务端的交互往往需要返回多条类型相同的数据,例如:从某个数据库返回一个记录集。这些数据集,在Beetle Ajax框架中是以列表形式返回的。框架专门在客户端构建了一个ArrayListJavascript自定义对象用来解析服务端返回的java.util.Listjava数据。

其使用示例如下:

下面的代码演示了在页面端提交用户列表数据给服务端接受并处理,然后再返回给页面,以及页面获取返回列表的解析情况。

页面代码:

//定义userinfo对象

function userInfo(username,pwd,sex,email){

  this.username=username;

  this.pwd=pwd;

  this.sex=sex;

  this.email=email;

  this.javaClass="ajaxdemo.vo.User";

}

function listShow(){

    var user1=new userInfo("Henry Yu","888888","M","henryyu@163.com");

    var user2=new userInfo("Tom Ma","777777","M","tomma@163.com");

    var user3=new userInfo("John Li","666666","M","johnli@163.com");

    var userList=new ArrayList();//创建一个List

    userList.add(user1);//list添加用户数据

    userList.add(user2);

    userList.add(user3);

    var req=new Request();

    req.setControllerName("DataTypeDemoAjaxController.ajax");

    req.put("flag",2);

    req.put("userList",userList.toObjectList());//ArrayList内数据转换成对象列表以便提交

    var r=req.synchroExecute();

    if(r.returnFlag==0){

      var reUserList=r.getValueAsArrayListByName("reUserList");

      for(i=0;i<reUserList.size();i++){//java.util.List一样解析返回的列表

          var user=reUserList.get(i);

         var username=user.username;

         var pwd=user.pwd;

         var sex=user.sex;

         var email=user.email;

         alert('-->用户信息<--\n姓名:'+username+'\n性别:'+sex+'\n邮件:'+email);

       }  

    }else{

      alert(r.returnMsg);

    }

}

Java代码:

package ajaxdemo.controller;

import java.util.List;

import ajaxdemo.vo.User;

import com.beetle.framework.web.controller.ajax.ControllerException;

import com.beetle.framework.web.controller.ajax.AjaxRequest;

import com.beetle.framework.web.controller.ajax.AjaxResponse;

import com.beetle.framework.web.controller.ajax.ICommonAjax;

public class DataTypeDemoController implements ICommonAjax {

    public AjaxResponse perform(AjaxRequest req) throws ControllerException {

       AjaxResponse res = new AjaxResponse();

       List userList=req.getParameterAsList("userList");

       for(int i=0;i<userList.size();i++){

           User user=(User)userList.get(i);

           StringBuffer sb = new StringBuffer();

           sb.append("echo:\n");

           sb.append("username:"+user.getUsername()+"\n");

           sb.append("pwd:"+user.getPwd()+"\n");

           sb.append("sex:"+user.getSex()+"\n");

           sb.append("email:"+user.getEmail()+"\n");

           System.out.println(sb.toString());

           user.setUsername("hi "+user.getUsername());//改变一下,以便测试

       }

       res.setValue("reUserList", userList);//传回给Javascript客户端

       return res;

    }

}

值得注意的是列表里面的元素应该保持类型一致,每个请求可以提交或返回多个列表,这一点没有任何限制。

3.4        复杂数据对象

对于复杂的数据对象,例如:包括了列表、值对象、数组等属性的值对象。Beetle Ajax对此类型的数据对象没有提供直接的支持。实际上,这涉及到序列化深度的问题,从技术角度上来说这个问题一直是rpc的挑战。很多著名协议如:SOAPHessian等对序列化深度都有一定的限制。

从设计角度来看,复杂的数据对象往往使得应用本身变得复杂化。奥卡姆剃刀原理,数据对象定义应该简单为好。所以,我们这里不推荐使用一个定义复杂的数据对象进行数据包装传输。当然,Beetle Ajax框架还是提供了一个getParameterAsJSONObject()方法以便开发人员可以自行解析自己定义的对象。

 

4.       数据解析与展示

Beetle Ajax框架只是解决了编程的模式化和数据传输的透明化。对于用户来说,最终关心的还是数据如何呈现以及交互界面友好性问题。我们知道,网页主要内容是通过Html来展现的。和Javascript一样Html也是一种语言。这事实上,产生了一个新的问题,就是Javascript数据如何内嵌到Html中以及Javascript如何动态操作Html的问题。

对于习惯了jsp编程的java程序员来说,往往对上述问题很困扰。

Html不是一个静态的标签组合吗,怎么还可以动态编程?”

Javascript不是简单用来作页面输入检验,它还能操纵Html?”

这些问题产生是传统的Web开发的重心在服务端,一般开发人员接触不到页面端DOM模型。JavascriptHtml之间的桥梁正是这个DOMDOM—Document Object Model,是W3C推出的标准化文档对象模型,通过DOMJavascript可以动态地对Html文档的内容、结构和样式进行访问和修改。

*HtmlJavascriptDOMAjax浏览器页面开发必须掌握的基础知识,否则,页面编程无从谈起。

4.1        基础DOM编程

涉及到DOM编程是一个很大的话题,限于篇幅和本文关心的重点,笔者在这里不打算系统讨论DOM,只是结合Beetle Ajax作个简单示例。关于DOM编程,有兴趣的读者可以参考Jeremy Keith的《Javascript DOM 编程艺术》,它是一本介绍DOM的不错著作。

4.1.1              示例1:(利用DOM操作页面元素,固定填充)

根据输入用户名称,从后台找出此用户信息,在页面显示出来。运行效果如下:

9 固定填充示例

页面Html代码:

<table width="100%" border="1">

     <tr>

        <td width="19%">用户名</td>

        <td width="81%">

<input type="text" name="username" isReq=1 message="用户名不能为空"  id="username"/></td>

     </tr>

     <tr>

        <td>密码</td>

        <td><input type="password"  name="pwd" isReq=1   id="pwd"/>&nbsp;</td>

     </tr>

      <tr><td>性别</td>

<td>

<input type="radio" id="sex1"  name="sex" isReq=1 message="必须选择"  value=M />

    <input type="radio"  id="sex2" name="sex" isReq=1 message="必须选择"  value=F />

     </td>

     </tr>

     <tr>

    <td>邮件地址</td>

    <td><input type="text" id="email" name="email" isReq=1 regex=EMAIL message="请输入一个有效的邮件地址"/></td>

     </tr>

     <tr>

            <td>教育背景</td>

            <td>

            <input type=checkbox name=education value=1 id="edu1" >高中

            <input type=checkbox name=education value=2 id="edu2">大专

            <input type=checkbox name=education value=3 checked id="edu3">本科

            <input type=checkbox name=education value=4 id="edu4">硕士

            <input type=checkbox name=education value=5 id="edu5">博士

            </td>

     </tr>

          <tr>

            <td>所在地区</td>

            <td> <select  name="area" isreq=1 message="请选择所在地区" id="area"/>

              <option value="">---请选择---</option>

            <option value=1>大陆</option>

            <option value=2>港台澳</option>

            <option value=3>国外</option></td>

          </tr>

     <tr>

        <td colspan="2"><div align="center">

        <INPUT TYPE="text" NAME="search" id="searchInput" isReq=1>['Henry' or 'Tom']

    <input type="button" name="Submit" value="查找" onclick="return showUserData()" >

                &nbsp;&nbsp; </div></td>

    </tr>

</table>

DOM中通过定义id来标识一个元素。

Javascript代码:

function showUserData(){

    //通过document.getElementById方法来找出某个页面元素,并通过此元素的value属性来获取它的值

    var si=document.getElementById("searchInput").value;

    if(si==""){

      alert("查询输入不能为空");

      return false;

    }

    var req=new Request();

    req.setControllerName("ShowDataAjaxController.ajax");

    req.put("flag",1);

    req.put("searchInput",si);//把参数放入Request对象

    var r=req.synchroExecute();//执行ajax请求并返回结果

    if(r.returnFlag==0){

      var user=r.getValueByName("userInfo");//获取后台返回的用户信息对象

       //通过dom定位页面id ”username”<input>元素,并设置它”value”属性的值

      document.getElementById("username").setAttribute("value",user.username);

      document.getElementById("pwd").setAttribute("value",user.pwd);

      var sex=user.sex;

      if(sex=="M"){

        document.getElementById("sex1").checked=true;

      }else{

        document.getElementById("sex2").checked=true;

      }

      document.getElementById("email").setAttribute("value",user.email);

      for(i=0;i<user.education.length;i++){

         document.getElementById("edu"+user.education[i]).checked=true;

      }

       //利用DOM操作页面<select>元素

      var sl_area=document.getElementById("area");

      var sl_opts=sl_area.options;

      for(i=0;i<sl_opts.length;i++){

          if(sl_opts[i].value==user.area){

                sl_opts[i].selected=true;

                break;

          }

      }

    }else{

      alert(r.returnMsg);

    }

}

java代码:

package ajaxdemo.controller;

import java.util.ArrayList;

import java.util.List;

import ajaxdemo.vo.User;

import com.beetle.framework.web.controller.ajax.ControllerException;

import com.beetle.framework.web.controller.ajax.AjaxRequest;

import com.beetle.framework.web.controller.ajax.AjaxResponse;

import com.beetle.framework.web.controller.ajax.ICommonAjax;

public class ShowDataController implements ICommonAjax {

    public AjaxResponse perform(AjaxRequest req) throws ControllerException {

        AjaxResponse res = new AjaxResponse();

        String query = req.getParameter("searchInput");

        if (query.equalsIgnoreCase("Henry")) {

            User user = new User();

            user.setArea(2);

            user.setEducation(new String[] { "1", "3", "5" });

            user.setEmail("henryyu@163.com");

            user.setPwd("888888");

            user.setSex("M");

            user.setRegistTime(new java.sql.Timestamp(System.currentTimeMillis()));

            user.setUsername("Henry");

            res.setValue("userInfo", user);

        } else if (query.equalsIgnoreCase("tom")) {

            User user = new User();

            user.setArea(3);

            user.setEducation(new String[] { "2", "3", "5" });

            user.setEmail("tom@163.com");

            user.setPwd("888888");

            user.setSex("F");

            user.setRegistTime(new java.sql.Timestamp(System.currentTimeMillis()));

            user.setUsername("Tom");

            res.setValue("userInfo", user);

        } else {

            res.setReturnFlag(1);

            res.setReturnMsg("没有查找到相关的数据!");

        }

        return res;

    }

}

4.1.2              示例2:(利用DOM动态生成表格)

只列出Javascript代码,其它省略,详细请参考本文例子源代码。

10动态生成表格

Javascript代码:

function showUserListData(){

    var req=new Request();

    req.setControllerName("ShowDataAjaxController.ajax");

    req.put("flag",2);

    var r=req.synchroExecute();

    if(r.returnFlag==0){

       var userList=r.getValueAsArrayListByName("userList");

       var tb_obj=document.getElementById("tb_obj");

       var tbody=document.getElementById("tbody_obj");

       var tcl=tbody.childNodes.length;

       for(i=1;i<tcl;i++){

          var a=tbody.lastChild;

          if(a!=null){

              tbody.removeChild(a);

          }else{

             break;

          }

       }

       for(i=0;i<userList.size();i++){

          var user=userList.get(i);

          var tr=   tbody.appendChild(document.createElement("tr"));

          tr.appendChild(document.createElement("td")).appendChild(document.createTextNode(i));

          tr.appendChild(document.createElement("td")).appendChild(document.createTextNode(user.username));

          tr.appendChild(document.createElement("td")).appendChild(document.createTextNode(user.pwd));

          tr.appendChild(document.createElement("td")).appendChild(document.createTextNode(user.sex));

          tr.appendChild(document.createElement("td")).appendChild(document.createTextNode(user.email));

          tr.appendChild(document.createElement("td")).appendChild(document.createTextNode(user.education));

          tr.appendChild(document.createElement("td")).appendChild(document.createTextNode(user.area));

      }

    }else{

      alert(r.returnMsg);

    }

}

 

4.2        jQuery编程

DOM的示例看到,DOM操作Html中规中矩的,缺乏灵活性。例如若想对页面元素添加一下动态事件或者修饰的话,很麻烦。所以,针对DOM的缺点,出现了不少很3方的Javascript库,jQuery是笔者认为其中最优秀的一个。它可以让你从沉闷DOM脚本和无聊重复Javascript编码工作中解放出来。jQuery的官方网站是http://www.jquery.com/有兴趣读者可以参考一下。

下面用jQuery来代替DOM,改写一下上面DOM的两个示例。两个示例中,Html代码和java代码都保持不变。

4.2.1              示例1:(利用jQuery,固定填充)

Javascript代码:

function showUserData(){

    var si=$("#searchInput").val();//获取元素的值

    if(si==""){

      alert("查询输入不能为空");

      return false;

    }

    var req=new Request();

    req.setControllerName("ShowDataAjaxController.ajax");

    req.put("flag",1);

    req.put("searchInput",si);

    var r=req.synchroExecute();

    if(r.returnFlag==0){

      var user=r.getValueByName("userInfo");

      $("#username").val(user.username);

      $("#pwd").val(user.pwd);

      var sex=user.sex;

      if(sex=="M"){

       $("#sex1").attr("checked",true);

      }else{

       $("#sex2").attr("checked",true);

      }

      $("#email").val(user.email);

      for(i=0;i<user.education.length;i++){

         $("#edu"+user.education[i]).attr("checked",true);

      }

      var sl_area=$("#area");

      var sl_opts=sl_area.attr("options");

      for(i=0;i<sl_opts.length;i++){

          if(sl_opts[i].value==user.area){

              sl_opts[i].selected=true;

              break;

         }

      }

    }else{

      alert(r.returnMsg);

    }

}

4.2.2              示例2:(利用jQuery,动态生成表格)

function showUserListData(){

    var req=new Request();

    req.setControllerName("ShowDataAjaxController.ajax");

    req.put("flag",2);

    var r=req.synchroExecute();

    if(r.returnFlag==0){

       var userList=r.getValueAsArrayListByName("userList");

       $tbody=$("#tbody_obj");

       for(i=0;i<userList.size();i++){

         var user=userList.get(i);

         $tr=$tbody.children("tr:first").clone();

          $("td:eq(0)",$tr).text(i);

          $("td:eq(1)",$tr).text(user.username);

          $("td:eq(2)",$tr).text(user.pwd);

          $("td:eq(3)",$tr).text(user.sex);

          $("td:eq(4)",$tr).text(user.email);

          $("td:eq(5)",$tr).text(user.education.toString());

         $("td:eq(6)",$tr).text(user.area);

         $tr.appendTo($tbody);

      }

      $tbody.children("tr:first").hide();

    }else{

      alert(r.returnMsg);

    }

}

可见,jQueryDOM来得简单、灵活。

*事实上,jQuery的内核也是基于DOM的。所以说学习DOM是基础。打个比喻:DOM就是金庸小说武林秘籍中的九阳神功,重在内功修为;jQuery就是九阴真经了,重在临场搏击,快、准、狠!

 

4.3        高级UI

解决了Javascript操作Html问题,理论上进行页面ajax编程就可以畅心所欲。但是为了满足被“宠坏”的用户日益高涨的页面UI要求,自己一行一行地手工编写Javascript是十分费神的工作。再说,自行编写的UI其重用性、扩展性、界面风格一致性等等都是巨大挑战。所以,对于一般软件开发商来说,寻找一套适合自己的Html高级UI组件,可以说是事半功倍。当前有很多Html UI开发包可供选择,其中,著名开源的产品有:

Dojo,网址是:http://www.dojotoolkit.org/

Ext,网址是:http://www.extjs.com/

等等。笔者较为喜欢Ext,它当前版本是2.0,这里就结合Beetle