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);