DWR(Direct Web Remoting)是一个开放源
码的使用 Apache 许可协议的解决方案,它包含服务器端 Java 库、一个 DWR Servlet 以及 JavaScript 库。虽然 DWR 不是 Java 平台上唯一可用的 Ajax-RPC 工具包,但是它是最成熟的,而且提供了许多有用的功能。为什么要使用DWR,我们首先介绍基本AJAX流程,从中可以看到引入DWR会带来什么好处。 DWR的标准流程如右图所示:
DWR框架基本介绍
从最简单的角度来说,DWR 是一个引擎,可以把服务器端 Java 对象的方法公开给 JavaScript 代码。使用 DWR 可以有效地从应用程序代码中把 Ajax 的全部请求-响应循环消除掉。这意味着客户端代码再也不需要直接处理 XMLHttpRequest 对象或者服务器的响应。不再需要编写对象的序列化代码或者使用第三方工具才能把对象变成 XML。甚至不再需要编写 servlet 代码把 Ajax 请求调整成对 Java 域对象的调用。
DWR 是作为 Web 应用程序中的 servlet 部署的。把它看作一个黑盒子,这个 servlet 有两个主要作用:首先,对于公开的每个类,DWR 动态地生成包含在 Web 页面中的 JavaScript。生成的 JavaScript 包含存根函数,代表 Java 类上的对应方法并在幕后执行 XMLHttpRequest。这些请求被发送给 DWR,这时它的第二个作用就是把请求翻译成服务器端 Java 对象上的方法调用并把方法的返回值放在 servlet 响应中发送回客户端,编码成 JavaScript。
页面触发eventHandler()事件,事件内部调用了AjaxService.getOptions方法,当调用完成后,利用服务端返回的数据用客户端的populateList()方法进行数据展现。
我们通过一个简单的DWR示例来说明如何使用DWR。
为了使用DWR,需要将DWR的jar文件拷入Web应用的WEB-INF/lib目录中(可在http://getahead.org/dwr下载),在web.xml中增加一个servlet声明,并创建DWR的配置文件。
服务端web.xml的配置:
<!– add the Servlet for DWR –>
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<description>Direct Web Remoter Servlet</description>
<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>500</param-value>
</init-param>
<init-param>
<param-name>logLevel</param-name>
<param-value>debug</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/dwr/dwr.xml</param-value>
</init-param>
<load-on-startup>6</load-on-startup>
</servlet>
<!– Action Servlet Mapping –>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
servlet-class值为uk.ltd.getahead.dwr.DWRServlet (如果dwr版本是1.0版本的,则必须用这个class)也可以是org.directwebremoting.servlet.DwrServlet
也可以使用精简的一份配置:
<!– add the Servlet for 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>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/dwr/dwr.xml</param-value>
</init-param>
</servlet>
<!– Action Servlet Mapping –>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
|
在WEB-INF中的dwr文件夹中创建文件dwr.xml:
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE dwr PUBLIC “-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN”
“http://getahead.org/dwr/dwr20.dtd”>
<dwr>
<allow>
<create creator=”new” javascript=”login”>
<param name=”class” value=”com.webex.tmis.test.Login” />
<include method=”sayHello” />
</create>
<convert converter=”bean” match=”com.webex.tmis.test.User”>
</convert>
</allow>
</dwr>
服务端创建Login.java和User.java
public class Login {
public User sayHello(String name) {
User user=new User();
user.setName(name);
user.setMessage(“Hello,”+name);
return user;
}
}
public class User {
private String name;
private String message;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
客户端的配置logon.jsp:
<%@ page language=”java” contentType=”text/html; charset=UTF-8″
pageEncoding=”UTF-8″%>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″>
<script type=’text/javascript’ src=’/Tms/dwr/interface/login.js’></script>
<script type=’text/javascript’ src=’/Tms/dwr/engine.js’></script>
<script type=’text/javascript’ src=’/Tms/dwr/util.js’></script>
<script>
function getDWRMessage () {
var value=document.getElementById(’user’).value;
//var bean= DWRUtil.getValues(value);
login.sayHello(value,showMessage);
}
function showMessage(user) {
if(user.name==’Allen’){
alert(user.message);
}else{
document.getElementById(’MessageSpan’).innerHTML = user.name+” is illegal!!!”;
}
}
</script>
<title>Logon</title>
</head>
<body>
<table>
<tr>
<td id=MessageSpan></td>
</tr>
<tr>
<td><input id=’user’ type=”text” value=”"></td>
</tr>
<tr>
<td><input id=’submitBtn’ type=”button” onClick=”getDWRMessage()”
value=”Submit”></td>
</tr>
</table>
</body>
</html>
|
效果如下:
做完配置后,可以加载http://127.0.0.1:8080/Tms/dwr看看哪些服务可以用。
Classes known to DWR:
- login (com.webex.tmis.test.Login)
|
如果你需要使用ajax完成表单提交的操作,那么你应该使用DWRUtil.getValues,参数或者是个form对象,或者是个与领域对象对应的js对象
DWR框加的高级话题
A.配置服务端方法的可调用范围
在以上的2个DWR示例中,我们配置了两个JAVA类,将它们的所有属性和方法都暴露给了客户端,为了提高安全性,我们通常在dwr.xml中应该只配置那些客户端需要使用的方法和属性。DWR支持对dwr.xml更细粒度的配置来提高安全性,我们先看一下与配置有关的两个元素:
create 元素
create 元素告诉 DWR 应当公开给 Ajax 请求的服务器端类,并定义 DWR 应当如何获得要进行远程的类的实例。这里的 creator 属性被设置为值 new,这意味着 DWR 应当调用类的默认构造函数来获得实例。其他的可能有:通过代码段用 Bean 脚本框架(Bean Scripting Framework,BSF)创建实例,或者通过与 IOC 容器 Spring 进行集成来获得实例。默认情况下,到 DWR 的 Ajax 请求会调用 creator,实例化的对象处于页面范围内,因此请求完成之后就不再可用。
create 的 javascript 属性指定从 JavaScript 代码访问对象时使用的名称。嵌套在 create 元素内的 param 元素指定 creator 要创建的 Java 类。最后,include 元素指定应当公开的方法的名称。显式地说明要公开的方法是避免偶然间允许访问有害功能的良好实践 —— 如果漏了这个元素,类的所有方法都会公开给远程调用。反过来,可以用 exclude 元素指定那些想防止被访问的方法。
convert 元素
convert 元素的作用是告诉 DWR 在服务器端 Java 对象表示和序列化的 JavaScript 之间如何转换数据类型。DWR 自动地在 Java 和 JavaScript 表示之间调整简单数据类型。这些类型包括 Java 原生类型和它们各自的类表示,还有 String、Date、数组和集合类型。DWR 也能把 JavaBean 转换成 JavaScript 表示,但是出于安全性的原因,做这件事要求显式的配置。
DWR分模块配置
一般来说,你只需要一个dwr.xml文件,并且放置在默认的位置:WEB-INF/dwr.xml。如果有大量的远程调用类,则可以将dwr.xml分成多个文件。 则在每web .xml中可以这样配置:
<servlet>
<servlet-name>dwr-user-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>config-user</param-name>
<param-value>WEB-INF/dwr-user.xml</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>dwr-admin-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>config-admin</param-name>
<param-value>WEB-INF/dwr-admin.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-admin-invoker</servlet-name>
<url-pattern>/dwradmin/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dwr-user-invoker</servlet-name>
<url-pattern>/dwruser/*</url-pattern>
</servlet-mapping>
DWR批量调用
在 DWR 中,可以在一个 HTTP 请求中向服务器发送多个远程调用。调用 DWREngine.beginBatch() 告诉 DWR 不要直接分派后续的远程调用,而是把它们组合到一个批请求中。DWREngine.endBatch() 调用则把批请求发送到服务器。远程调用在服务器端顺序执行,然后调用每个 JavaScript 回调。
批处理在两方面有助于降低延迟:第一,避免了为每个调用创建 XMLHttpRequest 对象并建立相关的 HTTP 连接的开销。第二,在生产环境中,Web 服务器不必处理过多的并发 HTTP 请求,改进了响应时间。
F.DWR同步与异步
在页面的执行中,如果在js中使用了dwr去远程调用数据,这时,下面的JS将会是继续执行,
如果你是用作校验的话,那就导致不同步问题,返回结果无法生效,
这时你可以通过设置DWR为同步来达到效果
DWREngine.setAsync(false); => 默认为异步,即 true;
调用完后,设置还原
DWREngine.setAsync(true);