Ajax-- 整合的力量

Ajax-- 整合的力量
2005 年,伴隨着 Web2.0 的東風, Ajax 逐漸進入國內開發人員的視線,並風靡全國。這個在 2005 年 2 月份由 Adaptive Path 公司的 Jesse James Garrett 命名的綜合技術,綜合利用 Javascript 、 XHTML 和 CSS 、 DOM 、 XMLHttpRequest (以下稱之爲 XHR )、 XML 和 XSTL 等技術,爲 Web 應用程序提供異步的 http 請求處理方式,幫助 Web 應用程序實現類似桌面應用程序的快速反應功能並提供更加優秀的用戶體驗效果。
從 Ajax 身上,可以很清晰的看到各種技術整合到一起之後所表現出來的非凡魅力:
ü       其使用 XHTML 和 CSS 實現標準化的呈現界面。
ü       其使用 DOM 實現動態的顯示和交互。
ü       其使用 XHR 實現與服務器的異步通信。
ü       其使用 Javascript 將 XHTML 、 DOM 、 XML 、 XHR 綁定。
不過要熟練使用 Ajax 進行開發,必須對 Javascript 、 XHR 相當熟悉,這對於不少程序員來說確實是個挑戰。隨着 Ajax 應用和研究的深入,各式各樣的開源框架、包、程序庫紛紛涌現。這些程序包和框架分別從各自的角度入手,試圖降低 Ajax 開發的難度,比如減少 Javascript 腳本的開發量,加快開發的速度。
就目前而言,開源 Ajax 程序包和框架主要從以下幾個方面對 Ajax 進行封裝和擴展:
ü       遠程調用工具包
遠程調用工具包是 Ajax 框架最底層的工具包,其通常使用自己的 API 封裝 XHR 對象,使得調用 XHR 更加簡單直觀。在 XHR 之前,我們通常使用內嵌的 IFRAME 來實現無刷新頁面發送 http 請求的效果。因此,這些遠程調用包必須支持那些不支持 XHR 的瀏覽器,以提高兼容性。
類似的工具包比如 Dojo 、 Prototype 等,他們都是純 Javascript 的,適用於任何的服務端程序語言。 DWR 、 Ajax.NET 、 JSON-RP 等則是基於代理的 Ajax 框架,其允許 Javascript 直接與後臺服務器端的類實現映射,是客戶端的 Javascript 可以通過他們直接訪問服務器對象。
ü       UI 工具包和組件
基於底層的遠程調用工具包,可以很容易的實現一些胖客戶端所需要的常用功能、組件和效果,比如目錄樹、標籤面板以及菜單。這些都是可複用的。典型的比如 Script.aculo.us ,其在 Prototype 的基礎上,創建了大量不同類型的 UI 效果,比如 Opacity (不透明性)、 Movement (移動)、 Highlight (高顯)等等。
ü       Web 應用程序工具包
這些工具包的目的在於模仿操作系統典型的 UI 效果,以創造與桌面應用程序更加接近的用戶體驗。典型的比如 SmartClient ,其所提供的組件能模擬 Windows 或者 Mac OSX 的 UI 效果。
ü       基於自定義標籤的工具包
這些工具包利用 JSP 支持自定義標籤的特點,通過自定義標籤封裝 Ajax 應用,使傳統的 HTML 控件在必要的時候具備 Ajax 功能,比如以異步方式處理表單的提交和響應,打開超鏈接內容的時候不重新加載頁面等等。通過這些工具包,我們能夠將複雜的功能封裝在 HTML 風格的特定標籤中,使用 Java 代碼自動處理格式化、訪問外部資源等 HTML 和 Javascript 所無法處理的任務。典型的比如 Ajax Tags 。
ü       Ajax 化的 Web 框架
當前主流的 Web 框架都意識到 Ajax 的價值所在,紛紛提供對 Ajax 的支持。逐漸的,我們也可以根據自己所使用的 Web 框架,從開源項目中尋找符合要求的 Web 框架擴展。比如, Struts Ajax Tags 就提供對 Struts 標籤的擴展。
ü       代碼動態生成
在 Ruby on Rails 社區, Ruby helper 功能能夠自動生成基於 Prototype 的 Javascript 代碼;而 Webwork2 ,則利用 Dojo 工具包,在 Java 平臺上實現同樣的功能。
ü       基於組件
在 .NET 領域,已經有 Ajax.NET 、 Atlas 等可複用的 Ajax 組件,其允許使用拖拉的方式在 IDE 的設計視圖中快速創建包含 Ajax 功能的組件;同樣的, Java 領域的 JSF 、 Tapestry 等框架也提供了類似的組件。這些組件提供了快速開發 Ajax 應用的另一捷徑。
在接下來的內容中,我們選取當前比較流行的三個開源程序包和框架,來體驗一下 Ajax 的魅力。
 
作者介紹:柯自聰,軟件工程師,專注於 Web 應用程序開發,關注 OA 、門戶、電子政務、電子商務領域,著有《 Ajax 開發精要 -- 概念、案例與框架》一書以及《 Ajax 開發簡略》、《 Liferay Portal 二次開發指南》等開源文檔。)
Prototype
2.1 什麼是Prototype
Prototype 是由 Sam Stephenson 開發的一個 Javascript 類庫,也是其他框架的鼻祖。其對現有的部分 Javascript 對象比如 Object 、 Function 、 Dom 、 String 等進行擴展,並且對 Ajax 應用進行封裝,藉此提供了兼容標準的更加易於使用的類庫,極大的方便開發人員快速創建具備高度交互性的 Web2.0 胖客戶端應用程序。
Prototype 最初的目標是應用於 Ruby 領域的,不過由於優秀的表現和完美的封裝以及服務器語言無關性,現在已經被應用到各個領域,包括 Java 、 .NET 、 PHP 等。不過在 Prototype 的源碼中,還是可以找到 Ruby 的影子,比如 Ruby 樣式的 Array 對象枚舉。
正如之前提到的, Prototype 是一個底層的遠程調用包,雖然其僅僅是一個千餘行的 Javascript 文件,但是它爲其他框架提供了最底層的 Javascript 擴展和 Ajax 封裝。其他 Javascript 程序庫在其基礎上構建了更加高級的功能和 UI 效果,比如 Script.aculo.us 。
Prototype 目前的最新版本是 1.4 , 1.5 也已經提供了 pre 版本,其官方網站提供了最新版本的下載,包括 zip 包、 js 文件和 Subvision 源碼。不過和其他版本一樣, Prototype 官方網站並未提供完整的參考文檔,開發者只能通過閱讀源碼掌握其功能。可喜的是,網上已經流傳着不少關於 Prototype 源碼解讀和使用的文檔,這在一定程度上彌補了 Prototype 官方文檔不足的遺憾。
2.2 軟件組織架構以及應用
Prototype 主要包括三個內容:一是提供了一些全局性的函數,替代原先煩瑣重複的代碼;二是對現有 Javascript 、 DOM 對象的擴展,提供訪問公共函數的捷徑;三是對 Ajax 應用的封裝,使得開發 Ajax 應用更加容易和快速。
全局性的函數,比較有代表性的 $ 系列函數和 Try.these() 函數。
$() 函數是用於替代在 DOM 中頻繁使用的 document.getElementById() 方法的,其返回參數傳入的 id 所指向的元素。不過,其允許傳入多個 id 作爲參數,然後返回一個其 id 指向的元素的 Array 對象。
$F() 函數則用於返回任何表單輸入控件的值,比如文本框、文本區域、下拉列表,其也是以元素的 id 或者元素本身作爲參數。不過,必須注意的是, id 所指向的元素必須支持 value 屬性,比如文本框。如果 id 指向一個按鈕,那自然就得不到所要的 value 值。

 
$A() 函數能夠將其接受到的任何可枚舉列表轉化成爲一個 Array 對象,比如將 string 字符串轉化成 Array 數組。 $H() 函數則將傳入的對象轉換成一個可枚舉的和聯合數組類似的 Hash 對象。 $R() 函數是 new ObjectRange(lowBound, upperBound, excludeBounds) 的縮寫和替代。
Try.thiese() 方法以一系列的函數作爲參數,按照順序一個一個的執行,返回第一個成功執行的函數的返回值。這使得想調用不同的方法直到其中一個成功執行的需求變得容易和直觀。否則我們就得變通的用 if else 去判斷了。典型的比如在保證瀏覽器兼容的情況下實例化 XHR 對象。
Prototype 對 Javascript 的 Object 、 Number 、 Function 、 String 、 Array 、 Event 等對象進行了擴展,創建了一些新的對象和類,並在此基礎上提供了很多有用的公共函數,比如 each() 、 any() 、 collect() 等。
Prototype 另外一個值得稱道的是對 Ajax 的封裝和簡化,這也是 Prototype 吸引我們的另外一個重要之處。 Prototype 的 Ajax 功能主要由 Ajax.Request 和 Ajax.Updater 兩個類完成。
在沒有使用 Prototype 之前,我們需要創建 XHR 對象實例並且異步的跟蹤其進程,在回調函數中使用 DOM 解析其返回的響應數據並且處理後更新頁面。而 Ajax.Request 類提供了完成這一系列步驟的捷徑。我們只需要將目標 URL 、 URL 參數、 http 請求方法類型、回調函數名稱等一股腦的傳遞給 Ajax.Request 類即可。
Ajax.Request 類是針對需要解析服務器返回的響應數據的情況。而如果服務器返回的信息已經是 HTML 格式,只需要填充到某個 HTML 控件中,則可以使用 Ajax.Updater 類。其調用 innerHTML 直接將 HTML 代碼填充到指定的 HTML 控件內部。
難得可貴的是,以往我們需要判斷 XHR 的 readyState 和 status 值來獲取 http 請求的狀態並且作出相應的處理,以便應付請求失敗的情況;而 AjaxRequest 和 Ajax.Updater 類提供了 onComplete 來替代這些煩瑣的判斷,其只需要簡單的在請求的選項參數中的名爲 onXXXX 屬性 / 方法中提供自定義的方法對象即可。
接下來,我們使用 Prototype1.4 ,列舉一二,體驗一下 Prototype 的主要功能及其所帶來的便捷。
2.3 循序漸近
從 Prototype 官方網站 http://prototype.conio.net 下載最新的開發包 prototype-1.4.0.js ,放到應用程序目錄中,通過 <script> 代碼引入 Prototype 程序庫:
<script language="javascript" type="text/javascript" src="prototype-1.4.0.js"></script>
2.3.1 $ 系列函數體驗
在 Prototype 出現之前,我們使用這種方式定位頁面上的某個 HTML 元素及其值:
var myElement = document.getElementById(“your element’s id”);
var myValue = document.getElementById(“your element’s id”).value;
現在,可以分別使用 $() 函數和 $F() 函數來代替,例程 1 展示 $() 和 $F() 函數的用法:
var myElement = $(“your element’s id”);
var myValue = $F(“your element’s id”);
 
例程 1 : $() 和 $F() 函數的用法
 
<p>Username:<input type="text" name="txtUsername" value="Jimmy"></p>
<p>
<input type="button" name="$Test" value=" $ " onClick="window.alert($('txtUsername'))">
<input type="button" name="$FTest" value=" $F " onClick="window.alert($F('txtUsername'))">
</p>
 
$A() 函數則將其接收到的可枚舉的任何參數轉化成爲一個 Array 對象。結合 Prototype 對 Array 的擴展, $A() 能夠提供更加強大的功能。例程 2 使用 $A() 函數獲取頁面中的全部 input 類型的控件,並使用擴展後的 each() 函數遍歷全部的控件。
 
例程 2 : $A() 函數的用法
 
<script language="javascript" type="text/javascript">
/*$A 函數體驗 */
function do$ATest() {
       var nodeList = document.getElementsByTagName("input");
       var nodeArray = $A(nodeList);
       var message = " 全部 input 控件: /r/n";
       nodeArray.each(
        function(node) {
            message += node.type + "|" + node.name + "|" + node.value + "/r/n";
        }
       );
       window.alert(message);
}
</script>
<input type="button" name="$ATest" value=" $A " onClick="do$ATest()">
2.3.2 Try.these() 函數的妙用
我們知道, XHR 是 Ajax 的核心之一。但是各個瀏覽器對 XHR 的實現不同, IE 瀏覽器的各個版本對 XHR 的支持也有所差異。爲了保證 Ajax 的瀏覽器兼容性,在實例化 XHR 對象的時候,通常要使用 try/catch 對兼容性進行判斷。比如例程 3 所示。
 
例程 3 :使用 try/catch 塊實例化 XHR
 
var xhr = null;
if(window.XMLHttpRequest) {
 xhr = new XMLHttpRequest();
 if(xhr.overrideMimeType) xhr.overrideMimeType(“text/xml”);
}
else if(window.ActiveXObject) {
 try {
    xhr = new ActiveXObject(“Msxml2.XMLHTTP”);
}catch(e) {
 try {
    xhr = new ActiveXObject(“Microsoft.XMLHTTP”);
}catch(e){}
}
}
 
而現在,使用 Try.these() 函數,這些煩瑣的過程變得異常簡單。
 
例程 4 :使用 Try.these() 函數實例化 XHR
 
function doInitialXHR() {
    return Try.these(
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
      function() {return new XMLHttpRequest()}
    ) || false;
}
2.3.3 對集合類的遍歷
之前提過, Prototype 最初的應用領域是 Ruby 。 Ruby 爲遍歷集合中的元素提供了一系列快捷的方法,使得執行維護、查找、收集、刪除其中的元素更加快速。 Prototype 新建了一個 Enumerable 對象,爲 Javascript 提供類似 Ruby 的功能。
在 Ruby 、 .NET 語言中,都支持使用 each 關鍵詞對集合中的元素進行遍歷。在 Enumberable 對象中,還有很多方法比如 all() 、 any() 、 collect() 等都是基於 each() 方法實現的。所以, each() 方法是操作集合元素的基礎。
each() 方法使用 iterator 依次獲取集合中的每個元素,並將其作爲匿名函數的參數;也可以在該匿名函數中加上可選參數 index ,獲取當前元素的索引值。其實在例程 2 中,我們已經使用了 each() 方法。
例程 5 使用 each() 方法,對一個保存貨物價格的數組進行遍歷,顯示價格及其索引值。
 
例程 5 :使用 each() 方法遍歷集合
 
function doEachTest() {
       var prices = [100.2, 445, 552.3, 56.0];
       prices.each(
        function(price, index) {
           window.alert("Value:" + price + "| Index:" + index);
        }
       );
}
2.3.4 P rototype Ajax體驗
Prototype 將 Ajax 應用封裝爲 Ajax.Request 和 Ajax.Update 類。使用這兩個類,可以應付大部分的 Ajax 應用,而且不需要煩瑣的實例化 XHR 、監控請求狀態的過程。
假設我們將書籍的信息保存在一個 XML 文檔中,如例程 6 所示。
 
例程 6 :保存書籍信息的 XML 文檔
 
<?xml version="1.0" encoding="gb2312"?>
<books>
 <book>
    <title>Ajax bible</title>
      <pages>500</pages>
 </book>
 <book>
    <title>Build with Ant</title>
    <pages>632</pages>
 </book>
 <book>
    <title>Google</title>
    <pages>934</pages>
 </book>
</books>
現在,我們使用 Ajax.Request 類去獲取這個 XML 文檔的內容,並將其顯示出來。例程 7 展示了這一過程。
 
例程 7 :使用 Ajax.Request 獲取 XML 文檔內容
 
<script language=”javascript” type=”text/javascript”>
/*Ajax.Request 類體驗 */
function doAjaxRequest() {
       var url = "books.xml";
       var myAjax = new Ajax.Request(
        url,
        {
        method:"get",
        onComplete:showResponse
        }
       );
}
/*Ajax.Request 類回調函數 */
function showResponse(request) {
 window.alert(request.responseText);
}
</script>
<input type="button" name="ajaxRequest" value="ajaxRequest" onClick="doAjaxRequest()">
圖 1 展示了使用 Ajax.Request 後所獲取的 books.xml 文檔內容。 
2-1.jpg
 
圖 1 使用 Ajax.Request 後所獲取的 books.xml 文檔內容
 
例程 7 中, onComplete 指定的 showResponse 函數其實是 Ajax 的回調函數,通常在這個回調函數中完成對響應數據的解析和顯示。而如果服務器端返回的是已經格式化後的 HTML 代碼(這點在 Ruby 中很流行),則可以使用 Ajax.Updater 。例程 8 使用 Ajax.Updater 將服務器的響應數據填充到指定的 div 中。圖 2 展示了使用 Ajax.Updater 的執行效果。
 
例程 8 :使用 Ajax.Updater 獲取服務器的響應數據
 
<script language=”javascript” type=”text/javascript”>
/*Ajax.Update 類體驗 */
function doAjaxUpdate() {
       var url = "response.jsp";
       var pars = "field=all&show=true";
       var myAjax = new Ajax.Updater(
        "divContent",
        url,
        {
        method:"get",
        parameters:pars
        }
       );
</script>
<input type="button" name="ajaxUpdate" value="ajaxUpdate" onClick="doAjaxUpdate()">
<p><div id="divContent"></div></p>
 
2-2.jpg
圖 2 使用 Ajax.Updater 的執行效果
 
例程 9 是例程 8 所請求的 JSP 文件。其簡單的打印出加粗後的“ Ajax.Update ”字樣。
 
例程 9
<%@ page contentType="text/html; charset=gb2312"
language="java" import="java.sql.*" errorPage="" %>
<%="<strong>Ajax.Update</strong>"%>
Dojo
3.1 什麼是Dojo
Dojo 是一個底層的 Javascript 工具包,其設計的目的是要使 Web 應用程序中增加動態特性更加容易和快捷。其提供了很多漂亮的窗口部件和 UI 效果,比如控件的淺入淺出、移動、拖拽、擦除、樹菜單、內容面板、標籤面板、面板容器、 Windows 窗口、嚮導窗口等等。 Dojo 所提供的強大的可靠的組件,可以使 Web 應用程序具備更加優秀的用戶體驗效果,響應更快,功能更多。
與 Prototype 相比, Dojo 提供了更完善豐富的文檔。除了完整的 Doc 文檔和 API 參考, Dojo 還提供了豐富的 Demo 示例,使得用戶更真切的體會到 Dojo 的應用效果。
與 Prototype 不同的是, Dojo 本身實現了很多 UI 效果,比如淺入淺出,而這些效果在 Prototype 是由其額外的擴展實現比如 Script.aculo.us 實現的。
Dojo 的最新版本是 0.3.1 。我們可以從其官方網站下載到最新的 zip 開發包,其中包含了豐富的 demo 示例。
3.2 軟件組織架構以及應用
Dojo 是由多個包組成的,其中包括:
ü       dojo.io :對 Ajax 應用進行封裝,提供簡單的 API 實現 Ajax 功能。
ü       dojo.event :提供瀏覽器兼容的事件體系。
ü       dojo.lang :用於支持混合( mixins )及擴展對象。
ü       dojo.graphics :用於實現優秀的 HTML 特效功能,比如淺入淺出、移動等等。
ü       dojo.dnd :用於支持拖拽功能。
ü       dojo.animation :用於支持動畫效果的創建。
ü       dojo.hostenv :用於提供 Javascript 擴展,比如提供 import/includes 替換原有的 src 屬性。
對於 Ajax 應用來說, Dojo 將其封裝爲一個簡單的方法, dojo.io.bind(request) 。其以一個 dojo.io.Request 對象作爲輸入參數,這個 dojo.io.Request 對象封裝了所有的 Ajax 操作信息,比如目標 URL 、請求類型、 mimetype 、是否採取異步操作、回調函數等等。 dojo.io.Request 對象比較常用的屬性包括:
url : Ajax 操作將提交 http 請求的目標 URL ;
mimetype : http 請求的 mime 類型,默認爲 text/plain ;
method : http 請求的類型, get 或者是 post ;
formNode : http 請求中使用的表單節點;
content :將附着在 http 請求中傳遞給服務器的內容;
changeUrl : Ajax 操作完成後將替換當前 URL 的新 URL ;
useCache :是否使用 Dojo 提供的緩存;
load :指定 Ajax 的回調函數;
error :指定 http 請求發生錯誤時要調用的函數;
timeout :指定 http 請求超時時要調用的函數;
除了 Ajax 封裝外, Dojo 所提供的窗口部件和 UI 效果也同樣引人注目。圖 3 和 4 展示了其中的兩個 UI 效果。
 
2-3.gif
圖 3 Dojo 所實現的標籤面板
 
2-3.jpg
 
圖 4 Dojo 所實現的仿 Windows 窗口
 
3.3 循序漸近
從 Dojo 的官方網站 http://dojotoolkit.com 下載最新的 Dojo 的 zip 開發包 dojo-0.3.1-ajax.zip ,解壓後的文件夾包含 demos 、 release 、 src 、 tests 子文件夾以及 dojo.js.uncompressed.js 、 dojo.js 等文件。其中, dojo.js.uncompressed.js 是未經過壓縮和代碼混淆的 Dojo 程序包, dojo.js 則是壓縮後的 Dojo 程序包。兩者在使用上沒有任何差異,不過要研究源碼還是參考前者爲好。
將 dojo.js 拷貝的程序目錄,通過 <script> 代碼引入:
<script language=”javascript” type=”text/javascript” src=”dojo.js”></script>
3.3.1   Dojo Ajax體驗
之前提到, Dojo 將 Ajax 應用封裝爲一個簡單的方法調用 dojo.io.bind() 。這裏,我們使用 dojo.io.bind() 來替換例程 7 所實現的功能:獲取並顯示服務器端的 xml 文檔。
dojo.io.bind() 封裝了 Ajax 的遠程調用功能,其獲取一個 hash 列表參數,隨後使用這個參數初始化 XHR 對象,然後在其本身註冊指定的回調函數。例程 10 展示了使用 dojo.io.bind() 實現例程 7 所示功能的過程。
 
例程 10 :使用 dojo.io.bind() 請求服務器的 XML 文檔
 
<script type="text/javascript">
dojo.require("dojo.event.*");
dojo.require("dojo.io.*");
/* 使用回調函數顯示響應數據 */
function doDojoBind() {
 var url = "books.xml";
 dojo.io.bind({
    url:url,
       load:function(type,data,evt) {showResponse(data);},
       error:function(type,error) {},
       mimetype:"text/plain"
 });
}
/*dojo.io.bind 使用的回調函數 */
function showResponse(data) {
       window.alert(data);
}
</script>
<input type="button" name="dojoIoTest" value="dojo.io.bind()" onClick="doDojoBind()">
DWR
4.1 什麼是JSON和DWR
在 Ajax 應用中,我們經常使用 XML 文檔來組織服務器的響應數據。雖然 XML 文檔在範式統一和數據表示等方面有不可比擬的優勢,不過將其應用於 Javascript 代碼中之前,必須經過 DOM 解析。 JSON ( Javascript Object Notation )是一種用簡單文本描述 Javascript 對象的開放格式標準,其使用 Javascript 數組直接量的格式來組織數據,相對 XML 文檔而言更易於組織和解析,而且很容易將 JSON 對象直接轉化成爲 Javascript 對象。比如例程 6 所示 XML 數據可以很直觀的用 JSON 格式表示,如例程 11 所示。這樣我們就可以很直觀的用 doc.books[0].title 來獲取第一本書的標題。
 
例程 11 :用 JSON 替代 XML 文檔表示數據
var doc =
{"books":
[
 {
 "title":"Ajax bible",
 "pages":500
 },
 {
 "title":"Build with Ant",
 "pages":632
 },
 {
 "title":"Google",
 "pages":934
 }
]}
如果要在 Ajax 應用程序中使用 JSON ,只需要以 JSON 格式組織服務器的響應數據, XHR 以文本的形式接收數據,則處理響應數據無需特定的數據解析器即可以使用 Javascript 操作數據了。
DWR ( Direct Web Remoting )是 getahead 公司開發的一個基於代理模式的 Ajax 應用框架,其允許客戶端 Javascript 遠程調用服務器端的 Java 類方法,執行相應的事務操作,就好像 Java 類是在本地客戶端上一樣。
DWR 使用自身的 API ,將遠程服務器上的 Java 對象或者列表格式數據轉化成爲 JSON 格式的 Javascript 本地對象,以供本地客戶機調用。
使用 DWR 可以有效的從應用程序代碼中把 Ajax 的全部請求 - 響應循環代碼消除掉,即客戶端再也不需要直接處理 XHR 對象,不再需要編寫對象的序列化代碼或者使用第三方工具才能將對象轉化成 XML ,甚至不再需要編寫 Java 代碼將 Ajax 請求調整成對 Java 對象的調用。
DWR 最新的版本是 1.1 ,其官方網站 http://getahead.ltd.uk/dwr/overview/dwr 提供了豐富的介紹、安裝、部署文檔以及足夠的 demo 程序包。
4.2 軟件組織架構以及應用
DWR 的結構主要包含兩部分,一是運行再瀏覽器客戶端的 Javascript ,其被用於與服務器通信;二是運行於服務器端的 Java Servlet ,其被用於處理請求並將響應結果轉化爲相應的格式回傳給瀏覽器。
DWR 採取一種動態生成基於 Java 類的 Javascript 代碼的新方法來實現和處理 Ajax 。這樣, Web 開發人員就可以在 Javascript 中像使用本地瀏覽器代碼一樣使用 Java 代碼,而實際上這些 Java 代碼是運行於服務器端的並且可以自由訪問 Web 服務器資源的。只是出於安全考慮, Web 開發者必須適當的配置,決定哪些 Java 類可以安全的被客戶端調用。
圖 5 展示了 DWR 如何利用一些類似 Javascript 的 onClick 等事件的結果來改變一個下拉框列表的內容。這個事件處理器調用一個 DWR 生成的 Javascript 函數,它和服務器端的 Java 函數是匹配的。 DWR 接着處理了 Java 和 Javascript 之間的所有遠程信息,包括轉換所有的參數和返回需要的值。接着 DWR 執行了相應的回調函數( populateList )。這個例子演示瞭如何使用 DWR 功能函數來改變網頁內容。
2-4.jpg

圖 5   DWR 交互過程
 
DWR 是作爲 Web 應用程序中的 servlet 部署的。把它看作一個黑盒子,這個 servlet 有兩個主要作用:首先,對於公開的每個類, DWR 動態地生成包含在 Web 頁面中的 Javascript 。生成的 Javascript 包含存根函數,代表 Java 類上的對應方法並在幕後執行 XMLHttpRequest 。這些請求被髮送給 DWR ,這時它的第二個作用就是把請求翻譯成服務器端 Java 對象上的方法調用並把方法的返回值放在 servlet 響應中發送回客戶端,編碼成 Javascript 。 DWR 還提供了幫助執行常見的用戶界面任務的 Javascript 工具函數。
4.3 循序漸近
有兩種方式可以開始 DWR 的應用。一種是直接從其官方網站下載 DWR 的 Web 應用示範包,這是一個 war 的部署包,從中可以對 DWR 的應用效果及其部署方式有一個大概的瞭解。不過這種方式無法詳細掌握如何將 DWR 與 Web 應用程序緊密集成。另外一種方式是根據 DWR 官方開發文檔的講解,通過一步步的部署和配置,將 DWR 集成到 Web 應用程序中。本節通過簡單的示範和一個例子,講述 DWR 的部署和集成。
DWR 採用一個 Java Servet 來處理請求並將響應結果發送給瀏覽器,這個 Java Servlet 需要加入到 Java Web 應用程序的部署描述文件 web.xml 。其次,其通過一個自定義的部署描述文件 dwr.xml ,控制 Java 對象與 Javascript 的轉化。
第一步:安裝 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 也可以從隨書光盤 software 目錄中找到。
第二步:修改 web.xml ,添加 Servlet 映射
修改 {APPLICATION_WEB_HOME}/WEB-INF 目錄下的 web.xml ,將下列代碼添加到 web.xml 的適當位置:
例程 12 :爲 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 部署描述文件,其代碼例程 13 所示:
例程 13 : 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 名稱還是有些限制的:
l         避免使用 Javascript 關鍵字或者保留字,因爲這些用 Javascript 關鍵字或者保留字命名的方法會自動執行。大部分的 Javascript 關鍵字或者保留字也是 Java 的關鍵字或者保留字,比如“ try ()”不是一個合法的命名。不過還是有一部分的 Javascript 關鍵字或者保留字在 Java 中不被限制,比如“ delete ()”。
l         避免使用方法重載。這些重載的方法被調用的是時候有時候會引起麻煩,因爲 Javascript 沒有像 Java 那樣的包命名機制來支持方法重載。
第四步:測試 URL ,查看部署效果
在瀏覽器地址欄中輸入 http://localhost:8080/ajaxlab/dwr ,其頁面效果應該如圖 6 所示。通常,這個頁面會顯式在 dwr.xml 部署描述文件中定義的全部 Java 類,並且顯式可以查看所有其可供遠程調用的方法的列表的鏈接。這個頁面有 DWR 動態創建。在本例中,頁面上有一個 JDate 的鏈接,列出暴露給 DWR 的 JDate 類可供 Javascript 遠程調用的方法。
 
圖 6   DWR 部署效果
 
點擊“ JDate ”鏈接,查看 Javascript 能夠調用的 java.util.JDate 類的方法,其效果如圖 7 所示。單擊每個方法後面的“ Execute ”按鈕,嘗試執行這些方法。
 
圖 7  能夠被 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>
例程 14 展示了這個過程,調用 java.util.Date 類的 toString ()方法將當前時間打印出來。這裏例子包含一個普通按鈕控件和兩個 Javascript 函數,其中一個函數 doTest 通過 JDate.toString ()調用,取得 java.util.Date 所表示的當前時間。第二個函數 responseDate 將第一個函數所取得的當前時間以彈出窗口的形式顯式出來。第二個函數的名稱作爲第一個函數的參數,這個類似第5章所說的回調函數。具體代碼如下,其運行效果如圖 8 所示:
例程 14 : sample12_1.jsp
 
<%@ page contentType="text/html; charset=gb2312" errorPage="" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Ch12--DWR 使用入門 </title>
<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>
<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>
 
 
 
圖 8  調用 JDate 的 Javascript 方法顯示當前時間
 
其他開源框架
AjaxAnywhere 是 sourceforge.net 另外一個開源的 Ajax 項目,其設計的初衷是要將已有的 JSP 和 JSF 組件轉換成爲具備 Ajax 功能的組件,而且這一轉換過程不需要複雜的 Javascript 編碼。與其他解決方案項目, AjaxAnywhere 不是基於組件的,比如在 AjaxAnywhere 就找不到類似 Ajax Tags 的輸入自動完成( auto-complete )組件。
使用 AjaxAnywhere 可以將頁面簡單的劃分爲多個區域( zone ),然後調用 AjaxAnywher 刷新( Refresh )那些指定的區域,而不是整個頁面。 AjaxAnywhere 使用“分區刷新”的思路,其工作原理如下:
1 、使用 AjaxAnywhere 自定義標籤庫將一個 Web 頁面劃分爲幾個可重載的區域( reload-capable zones )。
2 、使用 AjaxAnywhere Javascript 應用編程接口( API )替代傳統通信機制下表單提交方式。
3 、當請求在服務器端處理的時候,決定那些頁面區域可以刷新( refresh )。這個過程可以使用基於客戶端的 Javascript 或者基於服務器端的 AjaxAnywhere 應用編程接口( API )。
4 、在服務器端, AjaxAnywhere 會生成包含即將更新的 HTML 代碼的 XML 文檔。
5 、在客戶端, AjaxAnywhere Javascript 接受這個 XML 文檔,解析文檔,並更新指定的頁面區域。
採取這樣的設計思路,可以儘可能的降低 Javascript 代碼量,降低 Ajax 的開發門檻:
ü       不需要掌握和開發那麼多的 Javascript 代碼。
由於缺乏被廣泛接受的命名習慣、格式化規則和模式,使得 Javascript 編碼相對 Java/JSP 複雜許多。尤其在瀏覽器兼容性方面缺乏有效的調試和單元測試手段。使用 AjaxAnywhere 可以擺脫這些 Javascript 的複雜性。
ü       方便集成。
使用 AjaxAnywhere 不需要改變底層的應用程序代碼。
ü       降低技術風險。
可以隨時在傳統的通信機制和 Ajax 之間切換,允許 Web 應用程序同時支持兩種通信機制。
ü       平滑的兼容性。
再也不用在使用 Ajax 還是傳統的交互方式間搖擺了,使用 Ajax AnyWhere 的 Web 應用程序可以兼容兩種請求方式。
AjaxAnywhere 的客戶端腳本經過了 IE 、 Mozilla Firefox 和 Opera 等瀏覽器的兼容性測試,能夠最大程度的保證代碼的瀏覽器兼容性。
另外,還需要注意的 AjaxAnywhere 特性是, Ajax 接收到的 Ajax 代碼採用特殊的方式處理。 AjaxAnywhere 通過 eval(“”) 的方式執行這些 Javascript 代碼,也可以將所定義的 Javascript 函數保存在適當的上下文( Context )中。不過,在允許 Ajax 方式重載的頁面區域,不允許執行 document.write() 之類的 Javascript 語句。
允許重載的區域可能在提交請求之前就確定了,這種情況下需要重載客戶端的 AjaxAnywhere.getZonesToReload() 的 Javascript 函數,不需要額外的服務器邏輯處理。
如果希望 AjaxAnywhere 重載整個文檔,則重載後的 AjaxAnywhere.getZonesToReload() 函數必需返回“ document.all ”字符串。也可以在服務器端調用 AAUtils.setRefreshAll(true) 刷新整個頁面。
相應的, Ajax 請求中的 response.sendRedirect() 會被轉化成 Javascript 代碼的 location.replace() 命令。
5.2 Ajax Tags
Ajax Tags 是一組 Jsp 標籤,用來簡化 Ajax 技術在 JSP 頁面中的使用。其提供了一些常見功能的標籤如下拉列表級聯選擇,用戶在文本框中輸入字符自動從指定的數據中匹配用戶輸入的字符( Google Suggestion 功能,輸入建議)等。整個 Ajax Tags 構建在 JavaScript 框架之上。本節將簡要介紹 Ajax Tags ,包括其提供的幾個 Ajax 標籤,並利用這些標籤構建幾個簡單的應用。
Ajax Tags 是位於 sourceforge.net 上面的一個開源 Ajax 項目,其出發點在於應用標籤封裝的思想,實現一些常用的 Ajax 應用,提高開發效率。 Ajax Tags 提供一系列的標籤,簡化在 JSP 頁面中使用 Ajax 的過程。 Javascript 在 Ajax 中佔很大的比重,但實際上不少服務器端的開發人員掌握的 Javascript 知識並不完整(按照正常分工,頁面構建應該是網頁設計人員或者前端開發人員的職責)。 Ajax Tags 提供的標籤,能夠讓開發人員將 JSP 頁面與 Ajax 應用快速的集成整合。
Ajax Tags 支持如下情況下的無刷新頁面更新:
ü       基於文本框的輸入自動完成( autocomplete based on character input to an input field ),類似 Google Suggestion 的搜索建議功能。
ü       下拉列表級聯選擇( select box population based on selections made from another field ),即當選擇一個下拉列表時,另一個下拉列表的列表項則根據上一個下拉列表的情況自動更新。
ü       文本高顯提示( callout or balloon popups for highlighting content ),即當單擊某個鏈接的時候,在其周圍彈出文本框,顯示相關提示信息。
ü       更新表單域內容,包括文本標籤以及全部控件。
ü       圖像關聯( toggling images )。
ü       表單域狀態開啓和關閉( form field states on/off )。
Ajax Tags 的實現包含 Javascript 和 Java 類,其中, Java 類基於 JDK1.4 編譯,需要 Servlet 容器支持;而 Javascript 文件可以在 Firefox1.1+ 和 IE5.0 以上版本環境下運行。
SWATO ( Shift Web Application To ……)是一組可複用的易集成的 Javascript/Java 庫,可以快速的使 Web 應用程序的交互方式轉變成 Ajax 方式。其主要特點有:
ü       通過 SWATO ,服務器端的 Java 庫可以很方便的部署在支持 Servlet 2.3 的服務器上。
ü       其客戶端的 Javascript 庫可以運行在多種支持 XMLHttpRequest 的瀏覽器上。
ü       其使用 JSON 方法編組服務器上的 POJO 數據,很容易在任何 Javascript 環境中操作遠程 POJO 對象,不管是使用硬編碼還是與其他 Javascript 庫集成。
ü       其提供簡單的接口,使客戶端的 Javascript 可以直接操作服務器上暴露給客戶端的 POJO 對象。
ü       其使用多個可複用的控件( Javascript 模版、 Javascript 日誌等),使開發 Web 應用程序更加容易。
其提供簡單靈活的在 web.xml 中配置使用 Servlet 和 Filter (過濾器)的方式,集成 Spring 。
與 DWR 類似, SWATO 提供了通過客戶端 Javascript 直接調用遠程服務器端 Java 對象和 EJB 接口的實現。不同的是, SWATO 使用 JSON-RPC-Java 機制來實現客戶端 Javascript 遠程調用服務器端 Java 和 EJB 接口。
在 SWATO 中,客戶端接受的數據可以是 JSON(SWATO 的後臺是直接把 Java 對象映射成 JSON) ,也可以是 XML( 你可以從遠端 URL 中獲取 XML, 它在 SWATO 客戶端引擎中被轉化爲 JSON) 。所以對於開發人員來說,它在客戶端所要關心的只是 JavaScript 對象。 ( 結構與 Java 對象或 XML 結構對應 ) 。然後利用 SWATO 提供的一些視圖組件 (AutoSuggest, Select, Form) 進行渲染。對於自定義性比較強的視圖控件,你可以在前端使用 Template 引擎 (來自 TrimPath, 類似於 Velocity 的簡單語法),甚至可以把它封裝成你自己的組件,只要在其中實現 gotResult 與 gotError 函數,便可以很輕鬆的與 SWATO 遠程調用功能集成。
推薦書目(按出版順序排列)
6.1 Ajax 基礎教程
國內出版的第一本 Ajax 書籍,重點介紹 Ajax 及相關的工具和技術,主要內容包括 XHR 對象及其屬性和方法、發送請求和處理響應、構建完備的 Ajax 開發工具、使用 JsUnit 測試 JavaScript 、分析 JavaScript 調試工具和技術,以及 Ajax 開發模式和框架等,適合作爲 Ajax 的入門書籍閱讀。
6.2 Ajax 實戰
Amazon.com 暢銷書,目前 Ajax 領域最全面深入的著作。其中不僅有對於基礎知識的介紹,還有對於 Ajax 開發中重大的體系架構問題的深入探討,總結了大量 Ajax 開發中的設計模式,並討論了框架、安全性與性能等等。書中提供了幾個典型的例子,兼顧各種開發平臺,這些例子的代碼稍作修改就可以直接應用於項目開發之中。
6.3 Ajax 修煉之道Pragmatic Ajax-Web2.0入門
原書作者是 Ajaxian.com 的創始人, JavaOne 、 TheServerSide 等諸多高級別會議的演講者。本書作爲 Pragmatic 系列之一,從實踐出發,通過實例展示 Ajax 的諸多特性,手把手教你實現 Google Map 的絢麗效果。不僅教會你 Ajax 的技術細節,同時還帶你瞭解各種功能強大的主流 Ajax 工具包( Dojo 、 Prototype 、 Script.aculo.us 、 DWR 、 Backbase 、 SmartClient 、 Ajax.NET 、 SAJAX 、 JSON-RPC ),掌握 Ajax 實時查看源代碼的方法以及進行代碼調試的相應方法,學習 Ajax 的開發模式和框架。本書後半部分共用了五個章節,從服務器端編程的角度,詳細講述了 Ajax 同 PHP 、 Ruby On Rails 、 Java 和 .NET 等語言的融合,最後還介紹了與 Ajax 相類似的其他 RIA 技術以及 Ajax 的精彩未來( E4X, Canvas, SVG, 的相關應用)。本書秉承了《程序員修煉之道》的特點—— “從小工到專家”,各個層次的 web 開發人員都能從本書中獲益匪淺。
6.4 Ajax 開發精要-概念、案例與框架
本書從概念、案例與框架三個角度來詳細闡述 Ajax 開發技術,先介紹 Ajax 的由來、優勢及其在 Web 開發中的地位,接着詳細介紹 Ajax 的各項組成技術、封裝、開源和商業框架等各種相關知識,並提供豐富實用的開發案例和綜合案例,引導讀者一步步地瞭解並掌握利用 Ajax 進行 Web 應用程序開發的方方面面。
6.5 Ajax 高級程序設計
本書是關於 Ajax 技術、模式和使用場景的開發人員級教程,不僅介紹了 Ajax 的基本知識,還討論了 Ajax 模式和框架,同時針對 XML( 包括 XPath 和 XSLT) 、 RSS / Atom 、 Web 服務、 JSON 和 Web 用戶界面組件等主題,闡述瞭如何將 Ajax 與這些技術有效地結合在一起,並給出了一個使用 Ajax 開發的 Web Mail 系統完整實例。書中經典的實例、完整的源代碼,都將給讀者帶來“實戰”的指導。
參考資源網站
7.1 Ajax : A New Approach to Web Applications
點評: Jesse James Garrett 在這篇文章中第一次將這種融合多種技術的綜合技術簡稱爲 Ajax 。在這篇文章中, Jesse James Garrett 分析了現有 Web 應用程序的缺陷,定義了 Ajax 各個組成技術的職責,並且說明 Ajax 爲何如此不同, Ajax 都應用在什麼地方。最後, Jesse James Garrett 給出一份 FAQ ,解答人們對 Ajax 的各種問題。
7.2 Dojo 官方網站
7.3 Prototype 官方網站
7.4 DWR 官方網站
7.6 JSON-RPC 官方網站
7.7 Java 開源Ajax開發組件類別列表
點評: open-open.com 一個專門收集 Java 領域開源項目的網站。其將各個開源項目分門別類,提供項目介紹和導航。該頁面介紹了當前大部分的 Ajax 開源項目,從中可以一窺 Ajax 開源的大致現狀。
7.8 Ajax 中國
點評:《 Ajax 實戰》一書的譯者,國內最早關注 Ajax 應用的羣體之一。站點上包含了大量介紹 Ajax 技術以及 Ajax 應用心得和經驗的文章,值得 Ajax 開發人員參考和借鑑。
7.9 Ajax Pattern
點評: AjaxPatterns.org 是一個收集 Ajax 設計模式的站點,其已經發展成爲 Ajax 技術的 WIKI ,內容涉及 Ajax 設計模式、 Ajax 書籍介紹、頁面架構、框架、工具等等,是跟蹤 Ajax 技術、提高 Ajax 開發設計水平的理想場所。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章