11.1.2 DWR使用入门
有两种方式可以开始DWR的应用。一种是直接从其官方网站下载DWR的Web应用示范包,这是一个war的部署包,从中可以对DWR的应用效果及其部署方式有一个大概的了解。不过这种方式无法详细掌握如何将DWR与Web应用程序紧密集成。另外一种方式是根据DWR官方开发文档的讲解,通过一步步的部署和配置,将DWR集成到Web应用程序中。本节通过简单的示范和一个例子来讲述DWR的部署和集成。
DWR采用一个Java Servlet来处理请求并将响应结果发送给浏览器,这个Java Servlet需要加入到Java Web应用程序的部署描述文件web.xml。其次,它通过一个自定义的部署描述文件dwr.xml来控制Java对象与Javascript的转化。下面通过五步简单的配置,将DWR部署到2.4节创建的开发项目中。
第一步:安装jar开发包。
从DWR官方网站http://www.getahead.ltd.uk/dwr/下载DWR的开发包。这里采用DWR1.0,它是一个简单的名为dwr1.0.jar开发包。将这个开发包放到{APPLICATION_ WEB_HOME} \WEB-INF\lib目录下。如果使用DWR1.1,则下载的应该是DWR1.1的开发包。这个开发包中包含了DWR运行所需的全部Java类及相应的API。dwr1.0.jar也可以从随书光盘jar lib目录中找到。
第二步:修改web.xml,添加Servlet映射。
修改{APPLICATION_WEB_HOME}\WEB-INF目录下的web.xml,将下列代码添加到web.xml的适当位置。
例程11-1 为web.xml添加DWR映射
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class>uk.ltd.getahead.dwr.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>
<servlet>映射部分应该紧随web.xml中的其他<servlet>映射,<servlet-mapping>则紧随<servlet-mapping>部分。
这段部署描述告诉Web应用程序,全部以“/dwr/”起始的URL所指向的请求都交给uk.ldt.getahead.dwr.DWRServlet这个Java Servlet来处理。
第三步:创建dwr.xml
在{APPLICATION_WEB_HOME}\WEB-INF目录下创建dwr.xml部署描述文件,其代码例程11-2所示。
例程11-2 DWR部署描述文件dwr.xml
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
"http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
<allow>
<create creator="new" javascript="JDate">
<param name="class" value="java.util.Date"/>
</create>
</allow>
</dwr>
这个XML文档中用到了其对应的DTD文档,顶部的文档声明指明当前用到的是DWR1.0版本。如果使用DWR1.1,则应该相应地修改这个XML文档的文档声明。
这个部署描述文件定义什么样的Java 类可以被DWR应用创建并通过Javascript远程调用。在上面的部署描述中,定义了可以被DWR创建的Java类java.util.Date,并给这个类赋予一个Javascript名称JDate。通过修改dwr.xml,也可以将自定义的Java类暴露给Javascript远程调用。
需要注意的是,dwr部署描述为远程Java类拟定的Javascript名称还是有些限制的。
避免使用Javascript关键字或者保留字,因为这些用Javascript关键字或者保留字命名的方法会自动执行。大部分的Javascript关键字或者保留字也是Java的关键字或者保留字,比如,“try()”不是一个合法的命名。不过还是有一部分的Javascript关键字或者保留字在Java中不被限制,比如“delete()”。
避免使用方法重载。有时候,在调用这些重载的方法会引起麻烦,因为Javascript没有像Java那样的包命名机制来支持方法重载。
第四步:测试URL,查看部署效果。
在浏览器地址栏中输入http://localhost:8080/ajaxlab/dwr,其页面效果如图11-2所示。通常,这个页面会显式在dwr.xml部署描述文件中定义的全部Java类,并且显式可以查看所有其可供远程调用的方法的列表的链接。这个页面由DWR动态创建。在本例中,页面上有一个JDate的链接,列出暴露给DWR的JDate类可供Javascript远程调用的方法。

图11-2 DWR部署效果
单击“JDate”链接,查看Javascript能够调用的java.util.JDate类的方法,其效果如图11-3所示。单击每个方法后面的“Execute”按钮,尝试执行这些方法。

图11-3 能够被Javascript远程调用的方法
第五步:使用Javascript远程调用Java类的方法。
java.util.Date类的相关方法已经暴露出来供Javascript远程调用。将以下的Javascript引用代码添加到JSP或者HTML文件中,就可以在JSP或者HTML文件中直接调用第四步配置所暴露的java.util.Date类的方法了。
<script language="javascript" src="/ajaxlab/dwr/interface/JDate.js"> </script>
<script language="javascript" src='/ajaxlab/dwr/engine.js'></script>
<script language="javascript" src='/ajaxlab/dwr/util.js'></script>
例程11-3展示了这个过程,调用java.util.Date类的toString()方法将当前时间打印出来。这个例子包含一个普通按钮控件和两个Javascript函数,其中一个函数doTest通过JDate.toString()调用,取得java.util.Date所表示的当前时间。第二个函数responseDate将第一个函数所取得的当前时间以弹出窗口的形式显式出来。第二个函数的名称作为第一个函数的参数,这与第5章所说的回调函数类似。具体代码如例程11-3所示,其运行效果如图11-4所示。
例程11-3 sample12_1.jsp
<%@ page contentType="text/html; charset=gb2312" errorPage="" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Ch11--DWR使用入门</title>
<script language="javascript" src="/ajaxlab/dwr/interface/JDate.js"></s cript>
<script language="javascript" src='/ajaxlab/dwr/engine.js'></script>
<script language="javascript" src='/ajaxlab/dwr/util.js'></script>
<script language="javascript">
function doTest() {
JDate.toString(load);
}
function load(data) {
window.alert("Current Time:"+data);
}
</script>
</head>
<body>
<input type="button" name="count" value="cont" onClick="doTest()">
</body>
</html>

图11-4 调用JDate的Javascript方法显示当前时间
通过简单的配置,Javascript可以自由地远程调用Java对象的方法。接下来看看DWR是怎么实现上述设计的。DWR使用dwr.xml部署描述文档来定义Javascript对象和Java类之间的映射转化。首先了解一下dwr.xml这个DWR定义的部署描述文件的结构。从dwr.xml对应的DTD(http://www.getahead.ltd.uk/dwr/dwr10.dtd,版本1.0)文件中很容易知道dwr.xml的具体结构。从DWR的官方网站可以找到关于这个DTD的说明文档(http://get ahe ad.ltd.uk/dwr-demo/dtddoc/)。
所有部署描述文件的顶级根元素为dwr,其按照顺序可以包含以下三个子元素之一。
init:此元素定义那些在应用程序启动时作为DWR运行库所需的类自动加载并初始化的类。
allow:此元素定义那些允许客户端Javascript远程调用的Java类。
signatures:此元素签名必要的方法,当使用集合的时候,为转换器指定Java反射机制外的类别信息。
表11-1显示了dwr.xml的各个组成元素及其父元素、属性、子元素、用途等信息。
表11-1 dwr.xml组成元素及其属性和用途
|
元素名称 |
属性名称 |
父 元 素 |
用 途 |
|
dwr |
|
|
dwr.xml文档的根元素 |
|
init |
|
dwr |
定义那些在应用程序启动时作为DWR运行库所需的类自动加载并初始化的类 |
|
creator |
|
init |
定义供Javascript调用的新建对象的方法,即对象的构造方法 |
|
|
id |
creator |
用来惟一标识creator所创建的对象。必需属性 |
|
|
class |
creator |
应用creator元素定义的Java对象的完整名称。必需属性 |
|
converter |
|
init |
定义Javascript对象和Java对象之间新的转换方法。有些类有默认的转换机制,但有些类需要自定义转换机制 |
|
|
id |
converter |
用来惟一标识converter所创建的对象。必需属性 |
|
|
class |
converter |
应用converter元素定义的Java对象的完整名称。必需属性 |
|
allow |
|
dwr |
定义那些允许客户端Javascript远程调用的Java类 |
|
create |
|
allow |
定义允许创建的Java类,并为其指定一个Javascript名称,并定义DWR应当如何获得要进行远程的类的实例 |
|
|
creator |
create |
create元素所使用的构造方法名称。必需属性 |
续表
|
元素名称 |
属性名称 |
父 元 素 |
用 途 |
|
|
javascript |
create |
Java类暴露给浏览器调用的Javascript名称。必需属性 |
|
|
scope |
create |
create元素所创建的类的可用范围,默认为page。可选属性 |
|
param |
|
create |
指定create元素所需要的参数,比如其允许创建的Java类的名称 |
|
|
name |
param |
param元素所指定的参数名称。必需属性 |
|
|
value |
param |
param元素所指定的参数值。必需属性 |
|
include |
|
create |
指定应当公开的方法的名称。必需属性 |
|
exclude |
|
create |
指定那些想防止被访问的方法 |
|
auth |
|
create |
为暴露给浏览器的方法指定允许访问的角色 |
|
|
method |
auth |
指定需要访问角色限制的方法。必需属性 |
|
|
role |
auth |
指定允许访问的角色。必需属性 |
|
convert |
|
allow |
告诉DWR在服务器端 Java 对象表示和序列化的 Javascript之间如何转换数据类型 |
|
|
converter |
convert |
指定所使用的转换器的标识。必需属性 |
|
|
match |
convert |
与转换器所匹配的类名称。必需属性 |
|
param |
|
convert |
指定转换器所要包含的参数 |
|
|
name |
param |
param元素所指定的参数名称。必需属性 |
|
|
value |
param |
param元素所指定的参数值。必需属性 |
|
signatures |
|
dwr |
签名必要的方法,当使用集合的时候,为转换器指定Java反射机制之外的类别信息 |
create元素告诉DWR应当公开给Ajax请求的服务器端类,并定义DWR应当如何获得要进行远程的类的实例。这里的creator属性被设置为值new,这意味着DWR应当调用类的默认构造函数(无任何参数)来获得实例。其他的可能通过代码段用Bean脚本框架(Bean Scripting Framework,BSF)来创建实例,或者通过与IOC容器Spring进行集成来获得实例。默认情况下,到DWR的Ajax请求会调用creator,实例化的对象处于页面范围内,因此请求完成之后就不再可用。本节的例子中,在无状态的JDate情况下,这样很好。
create的javascript属性指定从Javascript代码访问对象时使用的名称。嵌套在create元素内的param元素指定creator 要创建的Java类。最后,include元素指定应当公开的方法的名称。显式地说明要公开的方法是避免偶然间允许访问有害功能的良好实践(如果漏了这个元素,类的所有方法都会公开给远程调用)。反过来,则可以用exclude元素指定那些想防止被访问的方法。
create元素的creator属性可有三种选择值:new,scripted,spring。最常见的是new选择值,它代表将使用Java类默认的无参数构造方法创建类的实例对象。scripted选择值则代表允许使用一些脚本语言,比如用BeanShell来创建Java类的实例对象,这在类无法通过配置远程调用的时候特别有用。spring选择值则允许远程调用spring Bean。当需要调用包含静态(static)方法的Java类时,建议使用new选择值。DWR会自动跳过(skip)静态方法。表11-2列出了当creator属性采用不同的选择值时,其对应param子元素的param和value的值及其含义。
表11-2 creator元素选择值及其含义
|
creator值 |
param值 |
value值 |
|
new |
class |
允许远程调用的类名称,包括完整的包名称 |
|
scripted |
language |
BSF支持的语言名称 |
|
scripted |
script |
返回供远程调用的对象的脚本 |
|
spring |
location* |
任何以location起始的参数名称。每个参数代表一个spring配置文件 |
|
spring |
beanName |
可从配置文件中读取的Bean的名称 |
为了安全考虑,将所有的Java类无选择地暴露给客户端Javascript是不理智的,所以必须加以控制和选择。create元素的include,exclude两个子元素用来对暴露给客户端Javascript的方法进行筛选,auth则能够将方法访问权限授予相应的角色对象(需要用到J2EE安全机制等)。注意,本节的示例第五步用到了一个src属性为“/ajaxlab/dwr/interface/ JDate.js”的Javascript文件。这个文件是DWR动态生成的。DWR根据dwr.xml的配置,将相应的Java类方法和属性转化为Javascript供客户端调用。所以在用户看来,就好像Javascript在调用本地对象一样。这些调用通过XMLHttpRequest对象转化为对uk.ltd.getahead.dwr.DWRServlet的请求,继而请求服务器响应。
create元素的creator属性负责公开用于Web远程的类和类的方法,convert元素的convertor属性则负责这些方法的参数和返回类型。convert元素的作用是告诉DWR在服务器端Java对象表示和序列化的Javascript之间如何转换数据类型。DWR自动地在Java和Javascript表示之间调整简单数据类型。这些类型包括Java原生类型和它们各自的类表示,还有String、Date、数组和集合类型。DWR也能把JavaBean转换成Javascript表示,但是为了安全考虑,做这件事要求显式的配置。
DWR应用中,所有的“/dwr/*”的请求都由这个DWRServlet来处理。当Web服务器启动的时候,DWRServlet调用init()方法完成初始化,设置日志级别、实例化DWRServlet用到的单例类、读取包括dwr.xml在内的配置文件。当“/dwr/*”请求送达的时候,DWRServlet的doGet,doPost方法调用processor.handle(req,res)方法,根据请求的URL完成如下事务。
请求URL为“/dwr/”根请求:将请求转向“/dwr/index.html”;
请求URL为“/dwr/index.html”:列出当前客户端可远程调用的全部Java类,页面仅供调试的时候使用;
请求URL为“/dwr/text/*”:为单独的Java类创建测试页面,页面仅供调试的时候使用;
请求URL为“/dwr/engine.js”或者“/dwr/uril.js”或者“/dwr/deprecated.js”:从dwr.jar包中读取相应Javascript文件的文件流,显示在页面上;
请求URL为“/dwr/interface/*”:将在dwr.xml中定义的Java类及其方法转化为Javascript函数;
请求URL为“/dwr/exec/”:处理远程Javascript请求;
请求URL为其他:设置http请求状态码为404,返回错误信息为“Page not found. In debug/test mode try viewing /[WEB-APP]/dwr/”。
既定的Java类已经通过DWR转化成可供客户端使用Javascript调用的方法和对象了。接下来就是使用Javascript调用这些暴露的方法和对象了。DWR最大的挑战在于如何将Java同步调用执行的特性与Ajax的异步特性结合起来。
DWR采用了XMLHttpRequest对象的回调机制来解决上述矛盾,即当响应数据从服务器返回是调用用户指定的回调函数。DWR提供两种方式为Javascript指定回调函数,即将回调函数名称加入参数列表,或者在参数列表中增加一个包含数据对象的调用。
将回调函数名称加入参数列表是最简单最常用的方式,本节的案例就使用了这种方式。假设Java类Remote提供的方法签名如下:
public class Remote() {
public String getData(int data) {……}
}
则可以在Web页面中使用如下方法调用:
<script language="javascript" src="{webapp}/dwr/interface/Remote.js" > </script>
<script language="javascript" src="{webapp}/dwr/engine.js"></script>
function handleData(str) {window.alert(str);} //创建回调函数
Remote.getData(100,handleData); //调用Javascript,100是Remote类方法getData本身的参数。
如果采用第二种方法,上面的调用代码可以这样书写:
Remote.getData(100,{callback:function(str){window.alert(str);}});
这种方法有时候具有比较好的可读性,也可以添加额外的调用,还可以为回调函数定时并设置错误处理函数:
Remote.getData(100,{
callback:function(str){window.alert(str);},
timeout:5000,
errorHandler:function(message){window.alert("No options:"+messag
e);}
});







