38
ajax 框架:dwr ext》实战 胡东峰 ajax 框架:dwr ext》实战 动手!动手!再动手!!! 先有效果,再讲道理!!! V0.1 2008-2-17 作者: 胡东峰 [email protected] 蓝杰信息 www.NetJava.cn 1

Ajax框架:Dwr》实战(包括整合)

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

《ajax框架:dwr与 ext》实战

动手!动手!再动手!!!

先有效果,再讲道理!!!

V0.1 2008-2-17

作者: 胡东峰

[email protected]

蓝杰信息 www.NetJava.cn 1

Page 2: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

目 录

7.DWR快速上手 .......................................................................................................................................................................3

1.DWR 简介 ................................................................................................................................................................................ 3 2.DWR 马上上手 : 在 JS 调用 JAVA 对象 .................................................................................................................................... 6 3.DWR 进阶 : LOADING 界面创建 ............................................................................................................................................ 11 4.DWR 进阶 : 线交流功能 ...................................................................................................................................................... 14 5.DWR 应用小结 ................................................................................................................................................................... 17

8.DWR综合实践:用户管理模块实现...................................................................................................................................18

1. 系统功能界面: ................................................................................................................................................................. 18 2. 编写后台业务实现: ......................................................................................................................................................... 20 3. 测试 DWR : .......................................................................................................................................................................... 22 4. 编写前台 DWR 调用代码: .............................................................................................................................................. 24 5. 集合类型的自动转换: ................................................................................................................................................... 27 6.JAVA 对象与 JS 对象的对接: .............................................................................................................................................. 27 7.DWR 中快捷的批赋值方法: .............................................................................................................................................. 28

9.DWR详解...............................................................................................................................................................................29

1.DWR 组件分析: ............................................................................................................................................................... 29 2.DWR 中的数据类型转换 ................................................................................................................................................... 30 3.DWR SERVLET 配置: ............................................................................................................................................................. 30 4.DWR 中的 SESSION 管理和页面转发 ...................................................................................................................................... 31 5.DWR.XML 配置 ....................................................................................................................................................................... 32 6. 使用 ANNOTATION 代替 DWR.XML 配置 .................................................................................................................................... 33 7.ENGINE.JS 说明: ................................................................................................................................................................... 34 8.UTIL.JS 说明: ....................................................................................................................................................................... 34

10.DWR+SP+HB整合:重构《员工日志管理》系统.............................................................................................................35

1.DWR+SP+HB 结构说明: ................................................................................................................................................ 35 2.DWR 与 SPRING 整合说明: ................................................................................................................................................... 36 3.DWR 与 HIBERNATE 整合说明: .............................................................................................................................................. 36 4.DWR 整合总结 : ..................................................................................................................................................................... 36 5. 任务与总结: ..................................................................................................................................................................... 37

11.关与 EXT...............................................................................................................................................................................37

1. 为什么需要 EXT? ................................................................................................................................................................. 37

蓝杰信息 www.NetJava.cn 2

Page 3: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

7.DWR 快速上手

1.dwr 简介

通过前面几节,我们己体验到了 ajax 应用的强大功能,但对与习惯了一种 OOP 语言编程的程序员来说: DOM、CSS、javascript、html,如何把这么多异构的东东协调的组合在一起,是一个十分头疼的问题;即使写好的 AJAX 应用,由与大量的使用了各种异构语言,系统的或维护性和清晰性也会变得困难。而对与每个 AJAX 应用而言,核心的功能是充当浏览器和服务器间的中间件----我们再从一个较高的层面抽像这个问题,后台以 java 语言为例:ajax 实现的功能是将 java 对象从服务器上取下来,显示到浏览器组件中。

创建 XMLHTTPRequest、发送请求、在指定的回调函数中解析服务器返回的数据(这个数据其实就是后台的某个 javaBean),更新页面;这三步是每个 AJAX 应用必不可少的。我们说过,一但工作有规则,就有可能用代码去 hack!这就是程序员的职则,与催生 MVC、ORM 等框架的原因同样,AJAX 也有很多现成的框架,让我们使用,让我们的将精力放在与业务相关的处理中而不是纠缠在 dom 或 js 的泥潭里。

DWR(Direct Web Remoting)就是这样的一个东东,请登陆http://getahead.org/dwr/ DWR allows Javascript in a browser to interact with Java on a server and helps you manipulate web pages with the results.如同 DWR图标所示的那样:

我猜想蓝、红箭头分别是代表了 javaScript 和javaBean,DWR做了中介:在页面的 js 中直接调用服务器上 javaBean,这可是个有创意的东东。

DWR主页上关与其的详细介绍如下:

DWR is a RPC library which makes it easy to call Java functions from JavaScript and to call JavaScript functions from Java (a.k.a Reverse Ajax).

蓝杰信息 www.NetJava.cn 3

Page 4: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

It has a large user-base, active mailing list and has been used in many projects including the Walmart shopping site and American Airlines flight booking site.

DWR has a number of features like call batching, marshalling of virtually any data-structure between Java and Javascript (including binary file uploading and downloading), exception handling, advanced CSRF protection and deep integration with several Java server-side technologies like Spring and Guice.

The first diagram shows how DWR can alter the contents of a selection list as a result of some Javascript event.

Reverse Ajax (available since DWR version 2.0) allows Java code running on the server to find out what clients are viewing certain pages, and to send to them JavaScript, generated either manually or using a Java API. These JavaScript generating APIs generally match a client-side APIs.

蓝杰信息 www.NetJava.cn 4

Page 5: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

DWR consists of two main parts:

• A Java Servlet running on the server that processes requests and sends responses back to the browser.

• JavaScript running in the browser that sends requests and can dynamically update the webpage.

DWR works by dynamically generating Javascript based on Java classes. The code does some Ajax magic to make it feel like the execution is happening on the browser, but in reality the server is executing the code and DWR is marshalling the data back and forwards.

This method of remoting functions from Java to JavaScript gives DWR users a feel much like conventional RPC mechanisms like RMI or SOAP, with the benefit that it runs over the web without requiring web-browser plug-ins.

The DWR project is developing a method of automatically creating Java versions of JavaScript APIs which developers can use to control browsers from the server. A server-side version of the TIBCO GI library is currently in alpha release, and the DWR project aims to expand this to cover other client side APIs including the Dojo Toolkit, JQuery, YUI, Ext and others.

For details on how to get started with DWR see the getting started guide and download instructions. 没错,这段文字是直接从 DWR网站上复制下来的,如果你看不懂这些英文,你就找个字典一个词一个词的看!要做程序员,你口语不标准可以接授,但如你看不懂简单的英文文档---那就

蓝杰信息 www.NetJava.cn 5

Page 6: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

是一个没有前途的程序员! 当然,如果你都能够看得懂,本节你就不必再向下看了,DWR主页上有详尽的教程和说明;关键那是原汁原味的!不像我这里写的,可能会有些走样。

2.DWR马上上手:在 js 调用 java 对象

如果你看到这里,只能说明你英文不好,学习能力差!所以我得马上用一个例子给件看DWR 能做些什么, 首先,从 DWR站上下载它的类库(不要告诉我你连dowload都找不着哦),下载列表有如下三种:

第一步:首先当然是在 ec 中新建项目,本例中为 netjavaDWR,将 dwr.jar加入到项目类库。 第二步:在 web.xml 中加入对 dwr主控 Servlet 的配置:<servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param></servlet>

<servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern></servlet-mapping> 这样,页面所有到 dwr路径请求的请求都会由 dwr 的 servlet 处理。第三步:编写一个要在游览器端调用的 java类:package cn.netjava.dwr;/** * DWR测试:这个类的对象将在js中调用 * @author www.NetJava.cn */public class FirstDWR {

/** * js中需要调用的测试方法

* @param userName:客户端传上来的字符串

* @return:模拟经过服务器处理的结果字符串

蓝杰信息 www.NetJava.cn 6

DWR 类库

DWR 演示程序,下后解到 womcat 的 webApps 下,

访问 http://localhost:8080/dwr 即可看到示例

源码了, java 的都有!

Page 7: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

*/public String sayHello(String userName){

return userName+" 您好!服务器时间是:"+System.currentTimeMillis();}

}第四步:要让 dwr找到这个类,还必须在 web-inf下创建 dwr.xml文件,并在其中配置上面的java类,dwr.xml内容如下:<dwr> <allow> <create creator="new" javascript="FirstDWR"> <param name="class" value="cn.netjava.dwr.FirstDWR"/> </create> </allow></dwr>第五步:测试己布置到 dwr 中的 java类调用。将这个项目布署到服务器上,请求http://localhost:8088/netjavaDWR/dwr/ 注意,主机、端口和应用程序名可根据你服务器实际填写,最后的 dwr路径会将请求发送到我们在 web.xml 中配置的 dwr 的 servlet 上。结果如下图:

注意看这里的对应关系,FirstDWR 就是我们在 dwr.xml 中配置的 javascript名字,后面括号里指示了它会调用那个类,点击 FirstDWR 会看到如下:

蓝杰信息 www.NetJava.cn 7

我们的 java 类的全包名

Page 8: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

这个图片太大了,得解释一下,

这部分指示了三个 js脚本,是我们在自己页面中调用 FirstDWR 对象时要引入的,其中的FirstDWR.js 是 DWR引擎自动生成的脚本。页面中可调用的方法列表如下:

蓝杰信息 www.NetJava.cn 8

Page 9: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

第一个 sayHello 就是我们在 FirstDWR类中定义的方法,点击 ,即可看到后面的输出:

---这就是 DWR 的核心功能---我们在客户端调用到了服务器上 java 对象的方法!但注意 sayHello 后面的一些方法,下面都有红色字体的 Warming,这是因为这些方法都是FirstDWR 从它的父类Object 中继承来的,但在 FirstDWR 中没有重写,所以暂时还无法调用(当然有的是办法,后面再说,呵呵)。 最后一步,就是在 html界面中调用了,请看如下界面效果:

testDWR.html 的页面代码及 js脚本如下:<html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK"> <link rel="stylesheet" type="text/css" href="generic.css" /><title>DWR入门 蓝杰信息@NetJava.cn</title></head>

<!--引入dwr的js脚本--> <script src="dwr/interface/FirstDWR.js"></script> <script src="dwr/engine.js" /></script> <script src="dwr/util.js" /></script>

<script type="text/javascript">/**响应界面事件,调用java对象的方法*/function updateResult() { //使用dwr工具类得到userName组件内的值

蓝杰信息 www.NetJava.cn 9

Page 10: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

var name = dwr.util.getValue("userName"); //调用服务器上的对象方法,并设定回调函数 FirstDWR.sayHello(name,callBack);}

//定义响应调用结果的回调函数var callBack=function(data) { var srcData=dwr.util.getValue("serverReplay"); dwr.util.setValue("serverReplay", data+"\r\n"+srcData); }; function testtt(theTab){ window.alert("你点击了!"+theTab.getAttribute("tabId")); }</script><body><pre>你的用户名:<input type="text" name="userName" onchange="updateResult();"/><br>服务器回应:<textarea cols="50" rows="5" name="serverReplay"></textarea><input type="button" value="清 除"> </pre> </body></html> DWR 中回调方法设置: 上面代码的关键是:FirstDWR.sayHello(name,callBack);这段代码传入了要调用的服务器上java 对象方法的参数,第二个参数是 sayHello方法处理完毕后的回调方法,等同与我们使用XMLHTTPRequest编写 AJAX 实现时编写的如下代码:

//指定请求对象的响应函数

sampleXMLHttpReq.onreadystatechange = processResponse;,

设定回调函数的规则是:1.如果第一个或最后一个是一个 js 函数,那么它就是回调函数,并且其他参数都是要调用的Java方法方法参数。2. 如果第一个参数是 null,测dwr认为没有回调函数,并且其他的都是 Java 方法参数;如果最后一个参数是 null,那么就没有 callback 函数。这里的 callBack 函数在 dwr 调用返回时会被自动调用以处理结果,此处不能写为Var fromserver=FirstDWR.sayHello(name); 为什么? 使用 DWR提供的工具函数: 在这段代码中,我们提取和设置 html 组件值都使用到了 dwr.util 这个 js 工具包,这个 js 中封装了许多常用的 js 函数,如我们在本例中用到的两个://设置对应名字的组件值:

dwr.util.setValue(组件名,组件值);//提取对应名字组件的值。

var theValue = dwr.util.getValue(组件名); 在随后的说明中,我们会更详细的介绍dwr.util 中的工具函数以简化AJAX开发。

蓝杰信息 www.NetJava.cn 10

Page 11: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

3.DWR进阶: loading界面创建

常见的一个需求是,当页面请求在服务器端需要较长处理时间时,在页面上最好给用户一个提示消息框,这称为 loading 页面,如果你还没看到过这个东东,请参看一下 gMail 的界面。

DWR 中要实现这个功能非常简单!简单得只需要一句话:dwr.util.useLoadingMessage("正在处理,请秒候. . .");

以上节为例,只需要在 updateResult()函数体中第一行加上这句话就是:/**响应界面事件,调用java对象的方法*/function updateResult() { //显示loading界面:

dwr.util.useLoadingMessage("正在处理,请秒候. . .") //使用dwr工具类得到userName组件内的值 var name = dwr.util.getValue("userName"); //调用服务器上的对象方法,并设定回调函数 FirstDWR.sayHello(name,callBack);}

OK,重测试,还看不到?那是因为太快了,它一闪而过---修改 FirstDWR 中的 sayHello方法如下:public String sayHello(String userName){

//为了前台显示loading界面,模拟任务占用较长时间try{

Thread.sleep(3000);}catch(Exception ef){}System.out.println("客户端发送的请求是: "+userName);count++;return count+" "+userName+" 您好!服务器时间是:"+System.currentTimeMil-

lis();}

让它执行时多用点时间而己,这下你看到了吧?

但这个 loading 可能还不会让你满意?其实它总是出现在屏的右上角;上图之所以好像是

蓝杰信息 www.NetJava.cn 11

Page 12: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

到了中间,其实是因为我放小了浏览器窗口而己。另外,它的颜色总是红的,如果能将一幅gif动画显示到 loading 中多好?这好办,我们查看 dwr.util.useLoadingMessage函数代码如下:

/** * Setup a GMail style loading message. * @see http://getahead.org/dwr/browser/util/useloadingmessage */dwr.util.useLoadingMessage = function(message) { var loadingMessage; if (message) loadingMessage = message; else loadingMessage = "Loading"; dwr.engine.setPreHook(function() { var disabledZone = dwr.util.byId('disabledZone'); if (!disabledZone) { disabledZone = document.createElement('div'); disabledZone.setAttribute('id', 'disabledZone'); disabledZone.style.position = "absolute"; disabledZone.style.zIndex = "1000"; disabledZone.style.left = "0px"; disabledZone.style.top = "0px"; disabledZone.style.width = "100%"; disabledZone.style.height = "100%"; document.body.appendChild(disabledZone); var messageZone = document.createElement('div'); messageZone.setAttribute('id', 'messageZone'); messageZone.style.position = "absolute"; messageZone.style.top = "0px"; messageZone.style.right = "0px"; messageZone.style.background = "red"; messageZone.style.color = "white"; messageZone.style.fontFamily = "Arial,Helvetica,sans-serif"; messageZone.style.padding = "4px"; disabledZone.appendChild(messageZone); var text = document.createTextNode(loadingMessage); messageZone.appendChild(text); dwr.util._disabledZoneUseCount = 1; } else { dwr.util.byId('messageZone').innerHTML = loadingMessage; disabledZone.style.visibility = 'visible'; dwr.util._disabledZoneUseCount++; } }); dwr.engine.setPostHook(function() { dwr.util._disabledZoneUseCount--; if (dwr.util._disabledZoneUseCount == 0) { dwr.util.byId('disabledZone').style.visibility = 'hidden'; } });};

可以在 dwr主站 http://getahead.org/dwr/browser/util/useloadingmessage 上查看这段代码的说明,老外真的诚实,说了这段是抄gmail 的,呵呵。如果你只是要修改显示文字的大小,颜色,位置,那么将这个函数复制出来改变其中 style 的值即可。这段代码好像很复杂,但我们如只要在这段代码中将它改为在 loading时传入一个图片地址做为参数,并让这个图片显示在窗体中心,就容易多了,修改后的函数接收一个图片地址,将图片显示出来即可,代码如下:

蓝杰信息 www.NetJava.cn 12

Page 13: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

/**载入loading动画,imageSrc:动画图片地址*/function useLoadingImage(imageSrc) { var loadingImage; if (imageSrc) loadingImage = imageSrc; else loadingImage = "ajax-loader.gif"; dwr.engine.setPreHook(function() { var disabledImageZone = $('disabledImageZone'); if (!disabledImageZone) { disabledImageZone = document.createElement('div'); disabledImageZone.setAttribute('id', 'disabledImageZone'); disabledImageZone.style.position = "absolute"; disabledImageZone.style.zIndex = "1000"; disabledImageZone.style.left = "0px"; disabledImageZone.style.top = "0px"; disabledImageZone.style.width = "100%"; disabledImageZone.style.height = "100%"; var imageZone = document.createElement('img'); imageZone.setAttribute('id','imageZone'); imageZone.setAttribute('src',imageSrc); imageZone.style.position = "absolute"; imageZone.style.top = "0px"; imageZone.style.right = "0px"; disabledImageZone.appendChild(imageZone); document.body.appendChild(disabledImageZone); } else { $('imageZone').src = imageSrc; disabledImageZone.style.visibility = 'visible'; } }); dwr.engine.setPostHook(function() { $('disabledImageZone').style.visibility = 'hidden'; });}

嗯,这段代也其实也是从 dwr站上抄来的,现在将这个函数放到页面中来试一下,事件处理代码做如下更改:/**响应界面事件,调用java对象的方法*/function updateResult() { //显示loading界面: //window.showModalDialog("index.jsp");

useLoadingImage("image/javaDancing.gif") //不用这个了。

//dwr.util.useLoadingMessage("正在处理,请秒候. . .") //使用dwr工具类得到userName组件内的值 var name = dwr.util.getValue("userName"); //调用服务器上的对象方法,并设定回调函数 FirstDWR.sayHello(name,callBack);}

看到的界面效果如下:

蓝杰信息 www.NetJava.cn 13

Page 14: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

在实际的应用中,可不能用这个 danceJava 的小人,需要美工做一幅合适的 gif动画。

4.DWR进阶:线交流功能

在 AJAX基础篇中,我们实现了一个在线聊天功能,其实的一个技术点就是让 js 定时请求轮询服务器,这种交互模式应用非常广泛,在 DWR 中实现这种轮询功能,与我们在一般的 js 中的定时请求不同;DWR 中的模式是由服务器端控制客户端消息显示,即 java 调用客户端的 js代码。 这个 demo 中的界面示例如下:

页面代码:<html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK">

蓝杰信息 www.NetJava.cn 14

Page 15: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

<link rel="stylesheet" type="text/css" href="generic.css" /><title>DWR入门--DWR后台调用页面组件 蓝杰信息@NetJava.cn</title></head>

<!--引入dwr的js脚本--> <script src="dwr/interface/ChatWithSamePage.js"></script> <script src="dwr/engine.js" /></script> <script src="dwr/util.js" /></script>

<script type="text/javascript"> function sendMessage() { ChatWithSamePage.sendWebMessage(dwr.util.getValue("text")); } </script><body><pre>发送内容:<input type="text" id="text" onkeypress="dwr.util.onReturn(event, sendMessage)"/><input type="button" value="Send" onclick="sendMessage();"/><hr><ul id="chatlog" style="list-style-type:none;"> </ul> </pre> </body></html>后台 java 代码由两个类组成,一个是简单的消息对象类:package cn.netjava.dwr;public class WebMessage {

/** * @param newtext the new message text */ public WebMessage(String newtext) { text = newtext;

if (text.length() > 256) { text = text.substring(0, 256)+". . ."; } }

/** * @return the message id */ public long getId() { return id; }

/** * @return the message itself */ public String getText() { return text; }

蓝杰信息 www.NetJava.cn 15

Page 16: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

/** * When the message was created */ private long id = System.currentTimeMillis(); /** * The text of the message */ private String text;}

另一个是与 js 相互调用的 ChatWithSamePage.java类,代码如下:package cn.netjava.dwr;import java.util.*;import org.directwebremoting.WebContext;import org.directwebremoting.WebContextFactory;import org.directwebremoting.proxy.dwr.Util;import org.directwebremoting.ScriptSession;/** * 通知消息调用java对象 */public class ChatWithSamePage {

/** * @param text The new message text to add */ public void sendWebMessage(String text) { WebContext wctx = WebContextFactory.get(); String remoteAdd= wctx.getHttpServletRequest().getRemoteAddr(); text="来自"+remoteAdd+"的朋友说:"+text; // Make sure we have a list of the list 10 messages if (text != null && text.trim().length() > 0) { messages.addFirst(new WebMessage(text)); while (messages.size() > 10) { messages.removeLast(); } } //将队列中的消息通知到所有客户端 notifyAllClient(wctx,messages); }

public void notifyAllClient(WebContext wctx,List msgQueue){ String currentPage = wctx.getCurrentPage(); // Clear the input box in the browser that kicked off this page only Util utilThis = new Util(wctx.getScriptSession()); utilThis.setValue("text", ""); // For all the browsers on the current page: Collection<ScriptSession> sessions = wctx.getScriptSessionsByPage(current-Page);

蓝杰信息 www.NetJava.cn 16

Page 17: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

Util utilAll = new Util(sessions); // Clear the list and add in the new set of messages utilAll.removeAllOptions("chatlog"); utilAll.addOptions("chatlog", messages, "text"); } /** * The current set of messages */ private LinkedList messages = new LinkedList();} 在 dwr.xml 中加入配置:<!-- 聊天消息调用接口 --> <create creator="new" javascript="ChatWithSamePage" scope="application"> <param name="class" value="cn.netjava.dwr.ChatWithSamePage"/> </create> <convert match="cn.netjava.dwr.WebMessage" converter="bean"/> OK,就这么简单!需要说明的是,这个例子算是剽窃DWR自带的 demo,之所以将它拿来出来,是因为:1.通过后台 java 代码调用而修改到前到 html 组件上的内容的做法很新颖;如代码中的:

Util utilThis = new Util(wctx.getScriptSession()); utilThis.setValue("text", "");. . .

utilAll.removeAllOptions("chatlog"); utilAll.addOptions("chatlog", messages, "text");. . .

2.对于持续的自动请求实现,这种模式可以将控制权放到 java 代码中,即可以让ChatWithSamePage类成为一个独立的线程,这样更加便于控制,在后面的案例中,实时页面曲线图的实现,就是使用 dwr 的这一机制。

3. WebContext 对象相当与 ServletContext 对象,它除了提供访问 dwr 对象的方法外,还可能过它直接得到 HttpServletRequest、HttpServletResponse、HttpSession、Servlet-Context、ServletConfig 等 servlet 对象。

需要注意的是,在精确的控制中,需要对 WebContext类深入的了解,例如如何确定页面的超时、如何给不同的用户发送不同的消息。

5.DWR 应用小结

通过上面这个例子,己展示了 dwr 的核心功能:即在浏览器端的 js透明的调用服务器上的 java对象。编写一个 dwr 应用,关键是如下几步: 1.编写要在前台调用的 java 对象; 2.在 dwr.xml 中配置这个对象;

3.就是在页面中引入dwr 的 js库及自动生成的调用 java 对象的 js库。在页面调用即可。 4.dwr 中不能调用后台的内部类对象。 5.在页面处理可,可以使用 dwr.util 中常用的 js 函数以快速开发。

使用 DWR 就这么简单,想想,它省下了我们多少事?

蓝杰信息 www.NetJava.cn 17

Page 18: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

8.DWR综合实践:用户管理模块实现

1.系统功能界面:

在前面的例子中,后台 java 对象返回的仅是一个字符串,那么前台 js 可不可以接受其它类型的返回值呢?当然是可以,dwr 中的 js 可以接收后台返回的 xml,字符串(整型)和集合类型及java 对象。本例中,主要示例 js 解析 java方法返回的集合类型,并展示 dwr.util 工具包更多的用法。

这个示例的需求也尽量的简法:用户在界面中输入用户名时,即可查服务服务器上的用户列表,并将用户数据展示在前台 table 中;前台用户可以增、删、改这些用户数据。用户界面是 userManger.html,初始界面如下:

当页面 onload时,下拉框中即装入从 UserManager.java 对象中调用 public Map getAreaMap()方法返回的地区列表填充下拉框: 当用户在下拉框中选择一个地区时,即触发其onChange事件,调用UserManager对象的

public List<UserInfo> getUserByAreaID(int areaID) 返回 UserInfo 对象的列表,填充表格:

蓝杰信息 www.NetJava.cn 18

Page 19: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

当用户点击修改按钮,表格下方将出现可修改的 input 组件,并让用户将修改后的值保存到服务器;如果用户点击删除按钮,将从表格中删除当前行,并调用服务器上的 public boolean deleteUser(int id)方法:

蓝杰信息 www.NetJava.cn 19

Page 20: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

2.编写后台业务实现:

后台由两个类实现,一个是简单的用户数据对象 UserInfo.java,用以保存用户对象属性数据,UserInfo.java 代码如下:/** * 用于DWR在前台展示的用户对象 * @author www.NetJava.cn */public class UserInfo{ private int id; private int areaID; private String name; private int fixFee; //for debug public String toString(){ return "id: "+id+" areaID:"+areaID+" name:"+name+" fixFee:"+fixFee; }// Getters, setters, equals omitted

蓝杰信息 www.NetJava.cn 20

Page 21: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

. . .}

第二个类是 UserManager.java,这个类是为前台 DWR提供调用方法,如取得地区列表,修改用户数据等,代码如下:package cn.netjava.dwr;import java.util.*;/** * 用户管理理:为dwr前端调用提供 * @author www.NetJava.cn */public class UserManger {

//取得用户的地区域列表:Map中为 区域ID:区域名public Map getAreaMap(){

return areaMap;}

// 根据地区代号提取用户列表public List<UserInfo> getUserByAreaID(int areaID){

System.out.println("AreaID : "+areaID);List<UserInfo> userList=new ArrayList();//模拟生成用户列表int count=new java.util.Random().nextInt(5)+4;for(int i=1;i<=count;i++){

userID++;UserInfo us=new UserInfo();us.setId(userID);us.setAreaID(areaID);us.setName("第"+userID+"个用户");us.setFixFee(100*i);userList.add(us);

}return userList;

}//以下方法模拟增、改、存用户到数据库public boolean updateUser(UserInfo user){

System.out.println("updateUser OK: "+user.toString());return true;

}

//根据用户id删除用户public boolean deleteUser(int id){

System.out.println("deleteUser OK: ID"+id);return true;

}

//区域列表,在类装载时初始化private static Map areaMap=new HashMap();static{

for(int i=0;i<5;i++){areaMap.put(1, "芙蓉区");areaMap.put(2, "红旗区");areaMap.put(3, "雨花区");areaMap.put(4, "开福区");

蓝杰信息 www.NetJava.cn 21

Page 22: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

areaMap.put(5, "岳麓区");}

}//用与生成用户唯一IDprivate static int userID=0;

}

OK,后台代码编写完毕后,接下来就是在 dwr.xml 中加入配置,将 java 对象可调用的方法暴露给 js 即可,dwr.xml 中加入如下配置:<dwr> <allow> <!-- 第一个dwr测试 --> <create creator="new" javascript="FirstDWR"> <param name="class" value="cn.netjava.dwr.FirstDWR"/> </create> <!-- 用户管理 cn.netjava.dwr.UserManager.java让js调用 --> <create creator="new" javascript="UserManger" scope="script"> <param name="class" value="cn.netjava.dwr.UserManger"/> </create> <convert match="cn.netjava.dwr.UserInfo" converter="bean"/> </allow> </dwr>

注意,这里出现了一个新的配置元素:<convert match="cn.netjava.dwr.UserInfo" converter="bean"/> 因为我们要在 js 中使用 UserInfo 对象,就必须通过 converter声明UserInfo 是一个符合 getter/setter 规范的 javaBean,这样,我们在页面中就可用 js 创建/使用UserInfo 对象。

3.测试dwr:

一般情况下,编写完后台逻辑、配置好 dwr.xml之后,必须测试;可以看出dwr 的测试相当方便:可直接在页面上调用后台对象可用的方法,并可即时看出结果。

蓝杰信息 www.NetJava.cn 22

Page 23: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

注意,涉及到 dwr 调用的 java 对象及其方法必须是 public 的,如本例中,如果 UserInfo类为非public,测试时将会看到如下错误:警告: Conversion error for java.util.ArrayList.org.directwebremoting.extend.MarshallException: Error marshalling cn.netjava.dwr.UserInfo: Class org.directwebremoting.impl.Proper-tyDescriptorProperty can not access a member of class cn.netjava.dwr.UserInfo with modifiers "public". See the logs for more details.

at org.directwebremoting.impl.PropertyDescriptorProperty.getValue(PropertyDescriptorProperty.java:70)

at org.directwebremoting.convert.BasicObjectConverter.convertOutbound(BasicObjectConverter.java:188)

at org.directwebremoting.dwrp.DefaultConverterManager.convertOutbound(DefaultConverterManager.java:192)

at org.directwebremoting.convert.CollectionConverter.convertOutbound(CollectionConverter.java:206). . .

测试完成后,开始编写 js 代码---什么时候能不写 js 就使用 ajax呢?

蓝杰信息 www.NetJava.cn 23

这里输入 java 方法接

受的参数

Page 24: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

4.编写前台 DWR 调用代码:

编写前台时,必须先确定 html界面,userManager.html界面代码如下:<html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK"> <link rel="stylesheet" type="text/css" href="generic.css" /><title>DWR示例:用户管理 蓝杰信息@NetJava.cn</title></head>

<!--引入dwr的js脚本--> <script src="dwr/interface/UserManger.js"></script> <script src="dwr/engine.js" /></script> <script src="dwr/util.js" /></script>

<script type="text/javascript"> //js脚本代码…</script>

<body onload="fillSelectUserArea();"><pre>根据区域提取未处理客户:<select id="selectUserArea" on-change="displayselectUserArea(this)"></select><table border="2"> <thead><tr> <th>序号</th><th>所属地区</th><th>用户名</th> <th>消费额度</th><th>操 作</th> </tr></thead> <tbody id="userTable"> </tbody></table><!-- 初始设为不可见 --><div id="editDiv" style="visibility:hidden">用户序号:<span id="spanuserid"> </span> 用户名字:<input id="iptname" type="text" size="10"/>所属地区:<input id="iptarea" type="text" size="10"/> 用户额度:<input id="iptfixFee" type="text" size="10"/> <br><input type="button" value="保 存" onclick="updateUser()"/><input type="button" val-ue="清 空" onclick="clearPerson()"/></div> </pre></body>

可以看出,界面上有许多事件调用,关键的几个事件说明如下;Js入口函数 用途 调用 java方法fillSelectUserArea(); 填充下区域接列表对象

selectUserAreak中的数据。public Map getAreaMap()

displayselectUserArea 填充用户列表数据到 userTable组件中。

public List<UserInfo> getUserByAreaID(int areaID)

updateUser() 将用户输入的数据封装为 js 中的一个 UserInfo 对象,发送到服务器保存。

public boolean updateUser(UserInfo user)

蓝杰信息 www.NetJava.cn 24

Page 25: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

OK,终与到关系的 dwr 调用实现的 js 代码了,如下:

//定义一个数组,用以缓存表中的用户信息var userCache = { };

//填充用户区域下拉表---DWR调用function fillSelectUserArea(){UserManger.getAreaMap(callBackFSU);}//填充用户区域下拉表DWR调用的回调方法var callBackFSU=function(areaList){DWRUtil.removeAllOptions("selectUserArea"); DWRUtil.addOptions("selectUserArea", areaList);};

//根据selectUserArea组件选择值调用填充表格的函数function displayselectUserArea(sua){ alert("选中的值是: "+sua.value); fillTable(sua.value); }

//从服务器上取取用户数据集合填充表格---DWR调用function fillTable(theAreaID){ if(theAreaID==null){ theAreaID=dwr.util.getValue("selectUserArea"); } UserManger.getUserByAreaID(theAreaID,callBack);}

//提取用户列表的回设函数:userList中放的是用户象var callBack=function(userList) { // Delete all the rows dwr.util.removeAllRows("userTable"); document.getElementById("editDiv").style.visibility ='hidden'; alert("返回用户列表长度是:"+userList.length); //取得html中表格对象 var dtable = document.getElementById("userTable"); for (var i = 0; i < userList.length; i++) { theUser = userList[i]; //在table中新建一行 var elTr = dtable.insertRow( -1 ); //注意这句:标识以后面删除时dom对象的唯一ID var userTableRowID=i*1000;

elTr.setAttribute("id", userTableRowID); //创建id列: var idTd = elTr.insertCell( -1 ); idTd.innerHTML = theUser.id; //创建areaID列 var areaTd = elTr.insertCell( -1 ); areaTd.innerHTML = theUser.areaID; //创建name列 var nameTd = elTr.insertCell( -1 ); nameTd.innerHTML = theUser.name;

蓝杰信息 www.NetJava.cn 25

Page 26: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

//创建name列 var fixFeeTd = elTr.insertCell( -1 ); fixFeeTd.innerHTML = theUser.fixFee; //创建编辑 var editTd = elTr.insertCell( -1 ); var editBTN="<input type=\"button\" value=\"修改\" onclick=\"editUser("+theUser.id+");\"/>"; var deleteBTN="<input type=\"button\" value=\"删除\" onclick=\"deleteUser("+theUser.id+","+userTableRowID+");\"/>"; editTd.innerHTML =editBTN +" "+deleteBTN; //将用户保存到缓存中 userCache[theUser.id] = theUser; } };

//修改某个用户的事件处理:参数为用户IDfunction editUser(userID){ document.getElementById("editDiv").style.visibility ='visible'; var user = userCache[userID]; alert("要修改的用户是: "+user.id); dwr.util.setValues({spanuserid:user.id,iptname:user.name, iptarea:user.areaID, iptfixFee:user.fixFee});}

//删除某个用户的事件处理---DWR调用function deleteUser(userID,userTableRowID) {alert("要修改的用户ID是: "+userID+"表中的行ID是: "+userTableRowID); var user = userCache[userID]; if (confirm("Are you sure you want to delete " + user.name + "?")) { var rowToDelete = document.getElementById(userTableRowID);

var userTable = document.getElementById("userTable"); //从表格中移除用户数据行 userTable.removeChild(rowToDelete);

UserManger.deleteUser(userID); }}

//更新用户事件处理---DWR调用function updateUser() { //创建一个js中的UserInfo对象,发送给服务器 var user=new Object(); user.id=dwr.util.getValue("spanuserid"); user.areaID=dwr.util.getValue("iptarea"); user.name=dwr.util.getValue("iptname"); user.fixFee=dwr.util.getValue("iptfixFee"); window.alert("要修改的用户ID为: "+user.id); UserManger.updateUser(user,callbackForUDUser);} //更新用户的回调函数var callbackForUDUser=function(result){ if(result){ fillTable(); window.alert("用户信息保存成功!"); }else{

蓝杰信息 www.NetJava.cn 26

Page 27: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

window.alert("用户信息保存失败!"); }};//清除输入框中的用户数据function clearUserInput() { dwr.util.setValues({spanuserid:null,iptname:null,iptarea:null, iptfixFee:null});}

这段代码中出现了许多新东东,我们来认识下:

5.集合类型的自动转换: 首先看到的是,dwr 中可以自动将 java 的 Map 对象中的键和值转化为 html seslect 组件中

的 option,如下代码://填充用户区域下拉表DWR调用的回调方法var callBackFSU=function(areaList){DWRUtil.removeAllOptions("selectUserArea"); DWRUtil.addOptions("selectUserArea", areaList);}; List 对象可以转换为 js 中的数组,在 js 数组中,每个对象依旧是 UserInfo 对象,且有其对应的属性名值://提取用户列表的回设函数:userList中放的是用户象var callBack=function(userList) { . . . for (var i = 0; i < userList.length; i++) { theUser = userList[i];

idTd.innerHTML = theUser.id; . . .

这些转换在 java 对象到 js 间都由中 dwr自动完成的,利用这一点,制做联动下拉框,是再简单不过的事了。

6.java 对象与 js 对象的对接:

本例中,dwr传送到界面上的数据结构是 UserInfo 对象,//定义一个数组,用以缓存表中的用户信息var userCache = { };//提取用户列表的回设函数:userList中放的是用户象var callBack=function(userList) {for (var i = 0; i < userList.length; i++) { theUser = userList[i];

//将用户保存到缓存中 userCache[theUser.id] = theUser; . . .}

//修改某个用户的事件处理:参数为用户IDfunction editUser(userID){ //得到的是js中的UserInfo对象 var user = userCache[userID]; alert("要修改的用户是: "+user.id);

蓝杰信息 www.NetJava.cn 27

Page 28: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

. . .} 可以看出,userList 中存入的 UserInfo 对象(java 中的),直接被转换为 js 中的 userInfo 对象,并在后来的数组中调用,其属性名值与生成的 java 对象是一致的。这是从 java-js 中的对象转换,从 js 到 java 也是一样的,看如下代码://更新用户事件处理---DWR调用function updateUser() { //创建一个js中的UserInfo对象,发送给服务器 var user=new Object(); user.id=dwr.util.getValue("spanuserid"); user.areaID=dwr.util.getValue("iptarea"); user.name=dwr.util.getValue("iptname"); user.fixFee=dwr.util.getValue("iptfixFee"); window.alert("要修改的用户ID为: "+user.id); UserManger.updateUser(user,callbackForUDUser);} 这段代码首先在 js 中创建了一个对象,并给对象的各个属性赋予用户输入的值,最后调用了UserManager.java 中的 updateUser方法,传入这个 js 的 UserInfo 对象,经过 dwr 处理后,这个对象会被转化为 java 代三中的 UserInfo 对象。 需要特别注意的是:js 与 java 中对应对应的属性名必须一致,大小写也一致!否则会怎样,你试下?

7.dwr 中快捷的批赋值方法:

dwr.util 中其实是 js 工具类的集合,在本例中我们看到了一种更简洁的页面组件赋值给 js 变量的方法://清除输入框中的用户数据function clearUserInput() { dwr.util.setValues({spanuserid:null,iptname:null,iptarea:null, iptfixFee:null});}//修改某个用户的事件处理:参数为用户IDfunction editUser(userID){//从js数组中取出用户对象 var user = userCache[userID]; alert("要修改的用户是: "+user.id); dwr.util.setValues({spanuserid:user.id,iptname:user.name, iptarea:user.areaID, iptfixFee:user.fixFee});} 即然可以批赋值,也可以以这种统一的方法将 html 组件值赋给 js 对象,即使用dwr.util.getValues 函数,详细说明请见 http://getahead.org/dwr/browser/util/getvalues 我就不说了(因为我没试成功:( ),

蓝杰信息 www.NetJava.cn 28

Page 29: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

9.DWR详解

1.DWR 组件分析:

DWR 框架解实现了 java方法与 js脚本之间的透明调用。DWR给程序员的界面由三个主要部分组成:

1.js脚本库:engine.js 是 dwr 的核心脚本库,发送 ajax 请还求,设定回调函数都必须用到这个库,

所以每个使用 dwr 的页面,都必须引入这个 js。Util.js 是 dwr 工具类库,它不是必须引入的,但一般情况下你都会用到。

2.DwrServlet: 这个 servlet 是服务器端处理所有 dwr 请求的入口,我们只需将其配置到 web.xml 中,注意

的是,这个 servlet 在 web.xml 中配置的 url-pattern 必须对应与要引入的 dwr 相关 js库的路径。 页面上发起的所有请求首先会由 DwrServlet接收,它会根据 dwr.xml 中的相关配置,输出转化后的对应 java 对象的 js脚本;在整个体系中,这个 Servlet 是核心组件。3.java 业务逻辑实现:

这部分不属与 dwr 框架,它是我们自己的业务实现 javaBean;javaBean被配置到 dwr.xml中,即可在客户端通过 js 调用其中的方法,或在这个 javaBean 中控制页面组件的实现---就需要到 dwr.jar 中的类库。

业务逻辑实现的 javaBean 可以是直接编码在包中的 java类,也可以是来自与hibernate、spring 等框架组装的对象;这些都可能过 dwr.xml 中的配置实现。

蓝杰信息 www.NetJava.cn 29

1.DWR JavaScript脚引本库dwr/engine.jsdwr/util.jsdwr/interface/YourJavaObj.js

2.DWR主控 servletDwrServlet dwr.xml

web.xml

3.java逻辑实现YourJavaObj.java

用户页面

YourJavaObj.doSt();

Dwr-lib.jar

Page 30: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

2.DWR 中的数据类型转换

我们可以将 DWR 框架框架看做一个 Convert,在 java 与 js之间转换其类数据型:对象、方法、参数/返回值类型转换。

Java 与 js之间默认转换的数据类型转换对应如下表:JavaScript Java

Array数组 List、Collection、数组

Boolean Boolean

Object Map、Java Bean

String String

Date Date

Numbers Int、double、float 等数值

XML Dom Dom

undefined null

符合 getter/setter 规范的 pojo要转换为 js 中的对象,需要在 dwr.xml 中另外配置,详见后面说明。特别注意的是:在 javabean 到 js Object 的转换中,1.后台的 javaBean 必须遵循 Java Bean 规范即实现Getter 和 Setter方法访问其对应属性。2.要输出到 js 中的 javaBean 必须在 dwr.xml 中配置其转换规则,即 Allow部分的 Convert元素<allow> . . . <!-- 用户管理 cn.netjava.dwr.UserManager.java让js调用 --> <create creator="new" javascript="UserManger" scope="script"> <param name="class" value="cn.netjava.dwr.UserManger"/> </create> <convert match="cn.netjava.dwr.UserInfo" converter="bean"/> . . . </allow>3.在 js 中接收到对应的 javaBean 对象时,js 对象的属性名与 javaBean 的属性名相同。4.可以利用 dwr.util.js 中的工具函数直接将后台反回的 Array、Collection、Map 对象赋与页面上的 select 或列表组件。

3.dwr Servlet配置:

Web 应用中要使用 dwr 的第一件事是在 web.xml 中配置dwr 的 servlet,一个较为全面的配置如下:<servlet>

蓝杰信息 www.NetJava.cn 30

JavaObj:

public class MyObj{ public int getAge(){. . .}

JsObj:

MyObj.js:MyObj.getAge(. . .);

DWR Convert

D

Page 31: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

<servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>initApplicationScopeCreatorsAtStartup</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>maxWaitAfterWrite</param-name> <param-value>100</param-value> </init-param></servlet>一些关键初始化参数说明为:name descdebug 设置成 true 使 DWR 能够debug 和进入测试页面,默认为 false。maxWaitingThreads DWR 调用最大等待线程数,默认为 100。welcomeFiles 类似于web.xml 的<welcome-file-list>标签,默认值为 index.html,

index.htm, index.jsp。logLevel 日志级别,可以是值可以是:FATAL,ERROR,WARN(默认),

INFO,DEBUGcrossDomainSessionSecurity 默认值为 true,如设成 false,就可以从其他域(webApp)进行请求 dwr,

这样不利与安全保护。 详细全面说明,请看dwr文档:http://getahead.org/dwr/server/servlet

4.dwr 中的 session管理和页面转发

Dwr 可以通过 js 调用后台 java方法返回某一个 url结果,如下代码示例://dwr将要在前台调用的返回某个页面数据的方法 destURL:目标页面 public String getInclude(String destURL) throws ServletException, IOException{ //取得webapp上下文对象 org.directwebremoting.WebContext web= WebContextFactory.get(); //返回页面结果 return web.forwardToString("/"+destURL); } 前台调用返回结果后,就可以直接将返回的内容填充到某个 div 中,如:/**前台计用java方法返回某个url数据*/function getDestURL(destURL) { //调用服务器上的对象方法,并设定回调函数 JavaClass.sayHello(destURL,callBack);}

//定义响应调用结果的回调函数:将返回的页面内容展示到destDiv层中var callBack=function(data) { document.getElementById("destDiv").innerHtml=data; };

蓝杰信息 www.NetJava.cn 31

Page 32: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

WebContext 对象是 dwr 在后台管理 web交互时的关键类,它通过 dwr 中的WebContextFactory工厂类得到;是扩展了 javax.servlet.ServletContext 的对象;通过WebContext 对象,我们可以得到前台请求这个方法时的 servlet 中的Request、Response、Session 等对象,如下代码示例://dwr将要在前台调用的返回某个页面数据的方法 destURL:目标页面 public String getInclude(String destURL) throws ServletException, IOException{ //取得webapp上下文对象 org.directwebremoting.WebContext web= WebContextFactory.get(); //得到servlet中的request/response对象 javax.servlet.http.HttpServletRequest request=web.getHttpServletRequest(); javax.servlet.http.HttpServletResponse response=web.getHttpServletResponse(); //在此可提取request中相关请求参数... //取得session对象 javax.servlet.http.HttpSession session=request.getSession(); //在此通过session做用户登陆等验证....

//返回页面结果 return web.forwardToString("/"+destURL); } 可以看出,dwr 中要控制会话管理,也是相关的简单,关键就是一个 WebContext 对象。但这种编程模型,似乎离我们前面所讲的 MVC 己相去甚远了。

5.dwr.xml配置

dwr.xml 中配置了所有要转换的 java 对象的规则,这个文件默认放置在 web-inf目录下;在dwr.xml 中配置的对象,可直接在页面进行测试。以下配置为例:<allow>

. . .<!-- 聊天消息调用接口 --> <create creator="new" javascript="ChatWithSamePage" scope="application"> <param name="class" value="cn.netjava.dwr.ChatWithSamePage"/>

<include method="sendWebMessage"/> </create> <convert match="cn.netjava.dwr.WebMessage" converter="bean"/> </allow>关键元素配置说明;1.<allow>元素:其中的每一个 create子元素定义了 DWR 能够创建和转换的类。2. <create>元素:定义一个让 DWR转换的 java类及转换规则 creator属性:默认为 new,意思是通过 new 关键字创建对象,被转换的 javaObj 必须有

一个无参构造器;如取值为 none,则不创建对象:一种情况是类中所有方法都是静态时使用,再就是对象在前面的 create 中己声明,则 scope 大于在此使用的,即使用己存在 webContext 中的对象。

Javascript:对象被转换后,在前台引入的 js脚本名字,这个脚本由 dwr 动态生成。 Scope元素:转换后对象存在的范围,与 Servlet 中的数据做用范围同理,取值可以是

application, session, request 和 page;默认为 page范围。 Include元素:默认情况下,class 中所有方法都会被 convter,include限定了只需要转

换可以前台调用 class 的方法,如上配置中,就只可调用 WebMessage

蓝杰信息 www.NetJava.cn 32

Page 33: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

中的 sendWebMessage方法。3.convert元素: 一般用于指定需要转换到前台的 javaBean 对象。其它常用数据类型,己由

dwr默认转换。 全面详尽的说明,请见 http://getahead.org/dwr/server/dwrxml 。

6.使用 annotation 代替dwr.xml配置

Annotation 是从 java5开始在语言级别提供的一项新特性,Annotation提供了一条与程序元素关联任何信息或者任何元数据(metadata)的途径。从某些方面看,annotation 就像修饰符一样被使用,并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在 annotation 的“name=value”结构对中。annotation类型是一种接口,能够通过 java反射API的方式提供对其信息的访问。

简单的说,Annotation 可以通过对类编写时的注解声明控制类对象运行时的规则(虽然我认为这会带来一定的混乱,即你看到的类源代码与运行时特征并不相符)。

在 dwr 中使用 Annotation 可以直接在 web.xml 中配置远程访问类和 bean,如下代码示:<servlet> <description>DWR controller servlet</description> <servlet-name>DWR controller servlet</servlet-name> <servlet-class> org.directwebremoting.servlet.DwrServlet </servlet-class> <init-param> <!-- 以下为可远程访问的java类和bean --> <param-name>classes</param-name> <param-value> com.example.RemoteFunctions, com.example.RemoteBean </param-value> </init-param> </servlet>

在编写 RemoteFunctions和 RemoteBean时,同样在其中要使用 dwr Annotation:

@Create(name = "Functions") public class RemoteFunctions {

//其中的方法即可被dwr导出前台访问

}要转换一个 bean 类可以被远程访问, 使用@Convert 和@RemoteProperty 标注:

@Convert public class Foo { @RemoteProperty private int foo; public int getFoo() { return foo; }

@RemoteProperty

蓝杰信息 www.NetJava.cn 33

这段注解说明这个类要前端访问

Page 34: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

public int getBar() { return foo * 42; }

}使用 dwr Annotation标注远程访问类要注意的是,编译级别的 jre 必须是 1.5以上。

7.engine.js 说明:

如其名所示,它是 dwr 中的核心 js库,在每个使用到 dwr 的页面,都必须引入!<script src="dwr/engine.js" /></script> <script src="dwr/util.js" /></script>DWR脚本一些全局属性名可以通过 engine.setXXX()设定,例如:

//设置调用超时DWREngine.setTimeout(1000);//将dwr设置为同步调用DWREngine.setAsync(true);关与这个脚本的详细说明,请见:http://getahead.org/dwr/browser/engine

8.util.js 说明:

Util.js 是 dwr 的前台工具类库,一个好处是,我们或以脱离dwr环境单独的使用它。它有4 个基本的操作页面的函数:getValue[s]()和 setValue[s]()可以操作大部分 HTML 元素了table,list 和 image。getText()可以操作 select list。 如果要修改 table 可以用 addRows()和 removeAllRows()。要修改列表(select 列表和 ul,ol 列表)可以用 addOptions()和removeAllOptions()。 其中常用的函数说明列表如下:函数 说明addOptions(id,valueArray)removeAllOptions(id)

添加,移除下接列表中的值。如:DWRUtil.addOptions(selectid, map)会将 map 中的 key(为 int)和 value(为 Seting)对创建到 select 中 option 组件的 value 和 text。

addRows(id) removeAllRows(id, array, cellfuncs, [options]);

操作 table 的函数,这两个函数的第一个参数都是table、tbody、thead、tfoot 的 id。一般来说最好使用 tbody,如removeAllRows(tbodyID)将移除表体中所有行。

onReturn 当按下 return 键时要调用的函数,例如:<input type="text" onkeypress="DWRUtil.onReturn(event,submitFunction)"/> <input type="button" onclick="submitFunction()"/>

selectRange(eleID,start,end)

选中输入框中文字的某部分。

getValue(id)setValue(id,value)

读取/设定页面中指定 ID 组件的值。可以操作 select、input、textArea、div和 span。

useLoadingMessage 等待消息,这个前面己经详细讨论过。

蓝杰信息 www.NetJava.cn 34

Page 35: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

Util.js 的详细说明请看dwr 在线文档:http://getahead.org/dwr/browser/util ;同时建议你阅读dwr针对某个对象生成的 js脚本源码,这样理解会更深入一些。

10.DWR+sp+hb整合:重构《员工日志管理》系统

1.DWR+SP+HB结构说明:

在实际的项目中,我们可以利用各种现成的框架实现快速开发,例如使用 hibernate映射数据库到 java 对象,在 spring 中提供业务实现的 javaBean装载,这些配置好的 jabaBean和 pojo 可以通过 dwr直接导出到前台调用,是不是很完美 ,dwr 中提供了这样的整合配置功能。整合后的系统结构如下图:

通过如上整合,让 sp负责加载业务处理的 javaBean,hb负责pojo 到数据库的映射,我们的工作就可用来只做最重要的事:动态页面的 js 编码和后业务逻辑的实现。

蓝杰信息 www.NetJava.cn 35

1.用户页面编码:

. . .引入相关js<script type="text/javascript">

function queryUser(userid){ var user=new Object(); user.id=userid; IUserinfoDAO.save(user,callBack);

Table:Userinfo

hibernate.cfg.xml: . . <mapping

resource="cn/netjava/pojo/Userinfo.hbm.xml" />

. . .

applicationContext.xml:<bean id="IUserinfoDAO". . .<property name="target"><ref local="UserinfoDAO" /></property>. . .

2.后台业务实现:

//Java Pojo:public class Userinfo{ private int id; // all Fields and //getter/setter . . .

dwr.xml:<allow> ... <create creator="spring" javascript="IUserinfoDAO"> <param name="beanName" value="IUserinfoDAO" /> <param name="location" value="applicationContext.xml" /> </create> <convert match="cn.netjava.pojo.*" converter="hibernate3"/></allow>

//Userinfo 的dao接口定义public interface IUserinfoDAO {//保存一个用户public boolean save(Userinfo ui);// 查找所有用户public List<Userinfo> findAll() ;

. . . }

Dwr+sp+hb整合结构示意图

Page 36: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

2.dwr 与 spring整合说明:

Dwr 与 sp整合只需要指示 dwr 使用 sp配置中的 bean 即可。<allow> . . .<!—说明使用spring创建bean--><create creator="spring" javascript="IUserinfoDAO"> <!—根据spring中配置的bean的id属性查找bean--><param name="beanName" value="IUserinfoDAO" /> <!—设定spring配置文件的位置名字--><param name="location" value="applicationContext.xml" /> </create> </allow>

如果 spring配置文件己在 web.xml 中配置,则此处不需再配置。直接在 web.xml 中配置 spring:<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>

3.dwr 与 hibernate整合说明:

我的理解是,dwr 不存在与 hb整合的概念---只是把 pojo 通过如下配置暴露出来而己,好像不关 hb 的什么事。<convert match="cn.netjava.pojo.*" converter="hibernate3"/> 这句配置中的 converter 使用了 hibernate3,如是 hb2,则改成对应版本号;这里其实也可以将改为 bean---就像转换非 hb 生成的 pojo那样,区别只是,hb 的 converter 不会去读pojo没有初始化的值。 注意,上面这句配置转化了所有数据库表的 pojo 对象到前台 js 中,如果你要排除某些属性的暴露,可以 create元素中加上排除参数,如下所示: <param name="exclude" value="propertyToExclude1, propertyToExclude2"/>

4.dwr整合总结:

现在流行整合框架风,所以只是要工具,拿起来第一要问的是怎整合?dwr 可以与任何框架整合,这取决与你的需求,它同样可以与 Struts整合---我实在看不出这样做的什么好处: <allow> ... <create creator="struts" javascript="ScriptName"> <param name="formBean" value="formBeanName" /> </create> ...

蓝杰信息 www.NetJava.cn 36

Page 37: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

</allow> 如果你非要在 dwr 前台调用 struts 的 action,那只能重构你的 Action 了。 说实在的,我看不出这种整合有什么必要。

你最好仅仅将 dwr 当做一个转换器使用;它在安全性上有所不足,这是由与它的灵活性所致,一般情况下,我们只能通过 create 中的 include 和 exclude 元素来控制远程调用 Bean 中可以被调用的方法。当然,高级的解决方案可以通过整合 Acegi 实现;另外,dwr它仅仅是使用异步方式在 js 和 java 对象间传送数据,漂亮的页面效果它也做不出----这就是我们最后的总结。

5.任务与总结:

1.一个类似的开源项目 json 也是通过提供 rpc机制实现页面调用 java 代码的, 利用它内置的一个轻级量 JSON-RPC JavaScripIt客户端,可以让你透明地在 JavaScript 中调用 Java 代码。JSON-RPC-Java 可运行在 Servlet容器中如 Tomcat 也可以运行在 JBoss 与其它 J2EE应用服务器中因此可以在一个基于 JavaScript 与 DHTML 的 Web 应用程序中利用它来直接调用普通Java方法与 EJB方法。项目主页:http://oss.metaparadigm.com/jsonrpc/ 。2.将我们前面完成的《员工日志管理系统》的某一个模块用 dwr+sp+hb重构。3.选择一款则重与界面展示的 AJAX 框架,构建一个简单demo,写总结分析其与 dwr 的适用方向及各自优劣点。

11.关与 ext

1.为什么需要 ext?

做为一名软件开发者,在使用 ajax技术开发应用时,大多的痛处是:漂亮的界面怎么搞定;大多数的专业 java 工程师开发的界面都是恐怖的;特别是在引入了 ajax技术后,页面表示己不像 mvc 中那样清晰;与美工良好的配合需要程序员对 css,html,javaScript技术更深入的掌握---而不仅仅是看懂为止,因此,更多时间,我们会在应用中使用像 dwr 这样成熟的 ajax 框架。 根以鄙人之见,ajax 框架可以分为如下四类型:

第一种是基与传输的解决方案,如我们前面所说到的 dwr,当然还有大名鼎鼎的 json;这种解决方案通过其框架的转换,使得页面的 js 可以透明调用服务器端语言方法并自动转换其间交换的数据类型;但其在页面的表现手段较差,如果你要展示一个漂亮互动的 tree 或 grid,这样样的框架并没有提供支持;另外就是它仅支持 java 语言的服务器端。

第二种可以理解为 java 语言到 js 的翻译器:即服务器端以编写事件调用机制通讯的 java代码,通过其框架导出成前台的 js脚本调用;对于开发者而言,只需要编写后端 java 代码即可;这种类型框架的噱头就是:“不写一行 js 代码也能应用 ajax技术!”。典型的代码如

蓝杰信息 www.NetJava.cn 37

Page 38: Ajax框架:Dwr》实战(包括整合)

《ajax 框架:dwr 与 ext》实战 胡东峰

GWT、ZK等,当然其缺点也是显示而易见的。 第三种则是以独立与服务器端语言的 js脚本库形式发布,如 yui、ext、jQuery等;这种基本上是一个独立的 js类库,带有众多的工具函组、tree、form、grid 等现成的组件,且有统一的设计风格;所以它的优势就是页面组件非常完善;当然,学习起来需要对 js 语法相当熟悉。 第四种则是全栈式设计的 RIA开发,可选的技术(平台)有 javaFX、Flex、及WPF 等。这种方式可以实现功能最为强大的 ajax开发----可以理解为它将传统的本机应用开搬到了 web 上。但其对网络带宽要求较高,且需要客户端浏览器插件支持。(http://www.riachina.com/ )上有相关较多的讨论。 本节我们之所以介绍 ext,就是利用它可以快速的做出让人惊呀的漂亮页面,又不至与让你有自己被变成一个美工设计者的感觉。马上下载 ext(http://www.extjs.com/download),到其examples 目录下看看那些漂亮的界面,如果你真的感觉到漂亮再继续向下走!

Ext 中文文档 http://www.jackytsu.com/extcn/docs/

蓝杰信息 www.NetJava.cn 38