免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
面向 Java 開(kāi)發(fā)人員的 Ajax: 結(jié)合 Direct Web Remoting 使用 Ajax

Philip McCarthy, 軟件開(kāi)發(fā)顧問(wèn), Independent Consultant

2005 年 12 月 27 日

雖然令人興奮,但是把 Ajax 功能添加到應(yīng)用程序可能意味著大量的艱苦工作。在面向 Java? 開(kāi)發(fā)人員的 Ajax 系列的第 3 篇文章中,Philip McCarthy 介紹了如何使用Direct Web Remoting(DWR)直接把 JavaBean 的方法公開(kāi)給 JavaScript 代碼并自動(dòng)進(jìn)行 Ajax 的繁重工作。

理解 Ajax 編程的基本知識(shí) 是重要的,但是如果正在構(gòu)建復(fù)雜的用戶界面,那么能夠在更高層次的抽象上工作也很重要。在面向 Java 開(kāi)發(fā)人員的 Ajax 系列的第 3 篇文章中,我在上個(gè)月的 Ajax 的數(shù)據(jù)序列化技術(shù) 基礎(chǔ)之上,介紹一種可以避免繁瑣的 Java 對(duì)象序列化細(xì)節(jié)的技術(shù)。

上一篇文章 中,我介紹了如何用 JavaScript 對(duì)象標(biāo)注(JSON)以一種在客戶機(jī)上容易轉(zhuǎn)化成 JavaScript 對(duì)象的格式對(duì)數(shù)據(jù)進(jìn)行序列化。有了這個(gè)設(shè)置,就可以用 JavaScript 代碼調(diào)用遠(yuǎn)程服務(wù),并在響應(yīng)中接收 JavaScript 對(duì)象圖,但是又不像遠(yuǎn)程過(guò)程調(diào)用。這一次,將學(xué)習(xí)如何更進(jìn)一步,使用一個(gè)框架,把從 JavaScript 客戶代碼對(duì)服務(wù)器端 Java 對(duì)象進(jìn)行遠(yuǎn)程調(diào)用的能力正式化。

DWR 是一個(gè)開(kāi)放源碼的使用 Apache 許可協(xié)議的解決方案,它包含服務(wù)器端 Java 庫(kù)、一個(gè) DWR servlet 以及 JavaScript 庫(kù)。雖然 DWR 不是 Java 平臺(tái)上唯一可用的 Ajax-RPC 工具包,但是它是最成熟的,而且提供了許多有用的功能。請(qǐng)參閱 參考資料,在繼續(xù)學(xué)習(xí)之前下載 DWR。

DWR 是什么?

從最簡(jiǎn)單的角度來(lái)說(shuō),DWR 是一個(gè)引擎,可以把服務(wù)器端 Java 對(duì)象的方法公開(kāi)給 JavaScript 代碼。使用 DWR 可以有效地從應(yīng)用程序代碼中把 Ajax 的全部請(qǐng)求-響應(yīng)循環(huán)消除掉。這意味著客戶端代碼再也不需要直接處理 XMLHttpRequest 對(duì)象或者服務(wù)器的響應(yīng)。不再需要編寫(xiě)對(duì)象的序列化代碼或者使用第三方工具才能把對(duì)象變成 XML。甚至不再需要編寫(xiě) servlet 代碼把 Ajax 請(qǐng)求調(diào)整成對(duì) Java 域?qū)ο蟮恼{(diào)用。

DWR 是作為 Web 應(yīng)用程序中的 servlet 部署的。把它看作一個(gè)黑盒子,這個(gè) servlet 有兩個(gè)主要作用:首先,對(duì)于公開(kāi)的每個(gè)類(lèi),DWR 動(dòng)態(tài)地生成包含在 Web 頁(yè)面中的 JavaScript。生成的 JavaScript 包含存根函數(shù),代表 Java 類(lèi)上的對(duì)應(yīng)方法并在幕后執(zhí)行 XMLHttpRequest。這些請(qǐng)求被發(fā)送給 DWR,這時(shí)它的第二個(gè)作用就是把請(qǐng)求翻譯成服務(wù)器端 Java 對(duì)象上的方法調(diào)用并把方法的返回值放在 servlet 響應(yīng)中發(fā)送回客戶端,編碼成 JavaScript。DWR 還提供了幫助執(zhí)行常見(jiàn)的用戶界面任務(wù)的 JavaScript 工具函數(shù)。





回頁(yè)首


關(guān)于示例

在更詳細(xì)地解釋 DWR 之前,我要介紹一個(gè)簡(jiǎn)單的示例場(chǎng)景。像在前一篇文章中一樣,我將采用一個(gè)基于在線商店的最小模型,這次包含一個(gè)基本的產(chǎn)品表示、一個(gè)可以包含產(chǎn)品商品的用戶購(gòu)物車(chē)以及一個(gè)從數(shù)據(jù)存儲(chǔ)查詢產(chǎn)品的數(shù)據(jù)訪問(wèn)對(duì)象(DAO)。Item 類(lèi)與前一篇文章中使用的一樣,但是不再實(shí)現(xiàn)任何手工序列化方法。圖 1 說(shuō)明了這個(gè)簡(jiǎn)單的設(shè)置:


圖 1. 說(shuō)明 Cart、CatalogDAO 和 Item 類(lèi)的類(lèi)圖

在這個(gè)場(chǎng)景中,我將演示兩個(gè)非常簡(jiǎn)單的用例。第一,用戶可以在目錄中執(zhí)行文本搜索并查看匹配的商品。第二,用戶可以添加商品到購(gòu)物車(chē)中并查看購(gòu)物車(chē)中商品的總價(jià)。





回頁(yè)首


實(shí)現(xiàn)目錄

DWR 應(yīng)用程序的起點(diǎn)是編寫(xiě)服務(wù)器端對(duì)象模型。在這個(gè)示例中,我從編寫(xiě) DAO 開(kāi)始,用它提供對(duì)產(chǎn)品目錄數(shù)據(jù)存儲(chǔ)的搜索功能。CatalogDAO.java 是一個(gè)簡(jiǎn)單的無(wú)狀態(tài)的類(lèi),有一個(gè)無(wú)參數(shù)的構(gòu)造函數(shù)。清單 1 顯示了我想要公開(kāi)給 Ajax 客戶的 Java 方法的簽名:


清單 1. 通過(guò) DWR 公開(kāi)的 CatalogDAO 方法
                        /**                        * Returns a list of items in the catalog that have                        *  names or descriptions matching the search expression                        * @param expression Text to search for in item names                        *  and descriptions                        * @return list of all matching items                        */                        public List<Item> findItems(String expression);                        /**                        * Returns the Item corresponding to a given Item ID                        * @param id The ID code of the item                        * @return the matching Item                        */                        public Item getItem(String id);                        

接下來(lái),我需要配置 DWR,告訴它 Ajax 客戶應(yīng)當(dāng)能夠構(gòu)建 CatalogDAO 并調(diào)用這些方法。我在清單 2 所示的 dwr.xml 配置文件中做這些事:


清單 2. 公開(kāi) CatalogDAO 方法的配置
                        <!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="catalog">                        <param name="class"                        value="developerworks.ajax.store.CatalogDAO"/>                        <include method="getItem"/>                        <include method="findItems"/>                        </create>                        <convert converter="bean"                        match="developerworks.ajax.store.Item">                        <param name="include"                        value="id,name,description,formattedPrice"/>                        </convert>                        </allow>                        </dwr>                        

dwr.xml 文檔的根元素是 dwr。在這個(gè)元素內(nèi)是 allow 元素,它指定 DWR 進(jìn)行遠(yuǎn)程的類(lèi)。allow 的兩個(gè)子元素是 createconvert。

create 元素

create 元素告訴 DWR 應(yīng)當(dāng)公開(kāi)給 Ajax 請(qǐng)求的服務(wù)器端類(lèi),并定義 DWR 應(yīng)當(dāng)如何獲得要進(jìn)行遠(yuǎn)程的類(lèi)的實(shí)例。這里的 creator 屬性被設(shè)置為值 new,這意味著 DWR 應(yīng)當(dāng)調(diào)用類(lèi)的默認(rèn)構(gòu)造函數(shù)來(lái)獲得實(shí)例。其他的可能有:通過(guò)代碼段用 Bean 腳本框架(Bean Scripting Framework,BSF)創(chuàng)建實(shí)例,或者通過(guò)與 IOC 容器 Spring 進(jìn)行集成來(lái)獲得實(shí)例。默認(rèn)情況下,到 DWR 的 Ajax 請(qǐng)求會(huì)調(diào)用 creator,實(shí)例化的對(duì)象處于頁(yè)面范圍內(nèi),因此請(qǐng)求完成之后就不再可用。在無(wú)狀態(tài)的 CatalogDAO 情況下,這樣很好。

createjavascript 屬性指定從 JavaScript 代碼訪問(wèn)對(duì)象時(shí)使用的名稱(chēng)。嵌套在 create 元素內(nèi)的 param 元素指定 creator 要?jiǎng)?chuàng)建的 Java 類(lèi)。最后,include 元素指定應(yīng)當(dāng)公開(kāi)的方法的名稱(chēng)。顯式地說(shuō)明要公開(kāi)的方法是避免偶然間允許訪問(wèn)有害功能的良好實(shí)踐 —— 如果漏了這個(gè)元素,類(lèi)的所有方法都會(huì)公開(kāi)給遠(yuǎn)程調(diào)用。反過(guò)來(lái),可以用 exclude 元素指定那些想防止被訪問(wèn)的方法。

convert 元素

creator 負(fù)責(zé)公開(kāi)用于 Web 遠(yuǎn)程的類(lèi)和類(lèi)的方法,convertor 則負(fù)責(zé)這些方法的參數(shù)和返回類(lèi)型。convert 元素的作用是告訴 DWR 在服務(wù)器端 Java 對(duì)象表示和序列化的 JavaScript 之間如何轉(zhuǎn)換數(shù)據(jù)類(lèi)型。

DWR 自動(dòng)地在 Java 和 JavaScript 表示之間調(diào)整簡(jiǎn)單數(shù)據(jù)類(lèi)型。這些類(lèi)型包括 Java 原生類(lèi)型和它們各自的類(lèi)表示,還有 String、Date、數(shù)組和集合類(lèi)型。DWR 也能把 JavaBean 轉(zhuǎn)換成 JavaScript 表示,但是出于安全性的原因,做這件事要求顯式的配置。

清單 2 中的 convert 元素告訴 DWR 用自己基于反射的 bean 轉(zhuǎn)換器處理 CatalogDAO 的公開(kāi)方法返回的 Item,并指定序列化中應(yīng)當(dāng)包含 Item 的哪個(gè)成員。成員的指定采用 JavaBean 命名規(guī)范,所以 DWR 會(huì)調(diào)用對(duì)應(yīng)的 get 方法。在這個(gè)示例中,我去掉了數(shù)字的 price 字段,而是包含了 formattedPrice 字段,它采用貨幣格式進(jìn)行顯示。

現(xiàn)在,我準(zhǔn)備把 dwr.xml 部署到 Web 應(yīng)用程序的 WEB-INF 目錄,在那里 DWR servlet 會(huì)讀取它。但是,在繼續(xù)之前,確保每件事都按照希望的那樣運(yùn)行是個(gè)好主意。





回頁(yè)首


測(cè)試部署

如果 DWRServletweb.xml 定義把 init-param debug 設(shè)置為 true,那么就啟用了 DWR 非常有幫助的測(cè)試模式。導(dǎo)航到 /{your-web-app}/dwr/ 會(huì)把 DWR 配置的要進(jìn)行遠(yuǎn)程的類(lèi)列表顯示出來(lái)。在其中點(diǎn)擊,會(huì)進(jìn)入指定類(lèi)的狀態(tài)屏幕。CatalogDAO 的 DWR 測(cè)試頁(yè)如圖 2 所示。除了提供粘貼到 Web 頁(yè)面的 script 標(biāo)記(指向 DWR 為類(lèi)生成的 JavaScript)之外,這個(gè)屏幕還提供了類(lèi)的方法列表。這個(gè)列表包括從類(lèi)的超類(lèi)繼承的方法,但是只有在 dwr.xml 中顯式地指定為遠(yuǎn)程的才標(biāo)記為可訪問(wèn)。


圖 2. CatalogDAO 的 DWR 測(cè)試頁(yè)

可以在可訪問(wèn)的方法旁邊的文本框中輸入?yún)?shù)值并點(diǎn)擊 Execute 按鈕調(diào)用方法。服務(wù)器的響應(yīng)將在警告框中用 JSON 標(biāo)注顯示出來(lái),如果是簡(jiǎn)單值,就會(huì)內(nèi)聯(lián)在方法旁邊直接顯示。這個(gè)測(cè)試頁(yè)非常有用。它們不僅允許檢查公開(kāi)了哪個(gè)類(lèi)和方法用于遠(yuǎn)程,還可以測(cè)試每個(gè)方法是否像預(yù)期的那樣工作。

如果對(duì)遠(yuǎn)程方法的工作感到滿意,就可以用 DWR 生成的 JavaScript 存根從客戶端代碼調(diào)用服務(wù)器端對(duì)象。





回頁(yè)首


調(diào)用遠(yuǎn)程對(duì)象

遠(yuǎn)程 Java 對(duì)象方法和對(duì)應(yīng)的 JavaScript 存根函數(shù)之間的映射很簡(jiǎn)單。通用的形式是 JavaScriptName.methodName(methodParams ..., callBack),其中 JavaScriptNamecreatorjavascript 屬性指定的名稱(chēng),methodParams 代表 Java 方法的 n 個(gè)參數(shù),callback 是要用 Java 方法的返回值調(diào)用的 JavaScript 函數(shù)。如果熟悉 Ajax,可以看出這個(gè)回調(diào)機(jī)制是 XMLHttpRequest 異步性的常用方式。

在示例場(chǎng)景中,我用清單 3 中的 JavaScript 函數(shù)執(zhí)行搜索,并用搜索結(jié)果更新用戶界面。這個(gè)清單還使用來(lái)自 DWR 的 util.js 的便捷函數(shù)。要特別說(shuō)明的是名為 $() 的 JavaScript 函數(shù),可以把它當(dāng)作 document.getElementById() 的加速版。錄入它當(dāng)然更容易。如果您使用過(guò) JavaScript 原型庫(kù),應(yīng)當(dāng)熟悉這個(gè)函數(shù)。


清單 3. 從客戶機(jī)調(diào)用遠(yuǎn)程的 findItems()
                        /*                        * Handles submission of the search form                        */                        function searchFormSubmitHandler() {                        // Obtain the search expression from the search field                        var searchexp = $("searchbox").value;                        // Call remoted DAO method, and specify callback function                        catalog.findItems(searchexp, displayItems);                        // Return false to suppress form submission                        return false;                        }                        /*                        * Displays a list of catalog items                        */                        function displayItems(items) {                        // Remove the currently displayed search results                        DWRUtil.removeAllRows("items");                        if (items.length == 0) {                        alert("No matching products found");                        $("catalog").style.visibility = "hidden";                        } else {                        DWRUtil.addRows("items",items,cellFunctions);                        $("catalog").style.visibility = "visible";                        }                        }

在上面的 searchFormSubmitHandler() 函數(shù)中,我們感興趣的代碼當(dāng)然是 catalog.findItems(searchexp, displayItems);。這一行代碼就是通過(guò)網(wǎng)絡(luò)向 DWR servlet 發(fā)送 XMLHttpRequest 并用遠(yuǎn)程對(duì)象的響應(yīng)調(diào)用 displayItems() 函數(shù)所需要的全部?jī)?nèi)容。

displayItems() 回調(diào)本身是由一個(gè) Item 數(shù)組表示調(diào)用的。這個(gè)數(shù)組傳遞給 DWRUtil.addRows() 便捷函數(shù),同時(shí)還有要填充的表的 ID 和一個(gè)函數(shù)數(shù)組。表中每行有多少單元格,這個(gè)數(shù)組中就有多少個(gè)函數(shù)。按照順序使用來(lái)自數(shù)組的 Item 逐個(gè)調(diào)用每個(gè)函數(shù),并用返回的內(nèi)容填充對(duì)應(yīng)的單元格。

在這個(gè)示例中,我想讓商品表中的每一行都顯示商品的名稱(chēng)、說(shuō)明和價(jià)格,并在最后一列顯示商品的 Add to Cart 按鈕。清單 4 顯示了實(shí)現(xiàn)這一功能的單元格函數(shù)數(shù)組:


清單 4. 填充商品表的單元格函數(shù)數(shù)組
                        /*                        * Array of functions to populate a row of the items table                        * using DWRUtil‘s addRows function                        */                        var cellFunctions = [                        function(item) { return item.name; },                        function(item) { return item.description; },                        function(item) { return item.formattedPrice; },                        function(item) {                        var btn = document.createElement("button");                        btn.innerHTML = "Add to cart";                        btn.itemId = item.id;                        btn.onclick = addToCartButtonHandler;                        return btn;                        }                        ];                        

前三個(gè)函數(shù)只是返回 dwr.xml 中 Itemconvertor 包含的字段內(nèi)容。最后一個(gè)函數(shù)創(chuàng)建一個(gè)按鈕,把 Item 的 ID 賦給它,并指定在點(diǎn)擊按鈕時(shí)應(yīng)當(dāng)調(diào)用名為 addToCartButtonHandler 的函數(shù)。這個(gè)函數(shù)是第二個(gè)用例的入口點(diǎn):向購(gòu)物車(chē)中添加 Item。





回頁(yè)首


實(shí)現(xiàn)購(gòu)物車(chē)

DWR 的安全性

DWR 設(shè)計(jì)時(shí)就考慮了安全性。使用 dwr.xml 明確地列出那些想做遠(yuǎn)程處理的類(lèi)和方法,可以避免意外地把那些可能被惡意利用的功能公開(kāi)出去。除此之外,使用調(diào)試測(cè)試模式,可以容易地審計(jì)所有公開(kāi)到 Web 上的類(lèi)和方法。

DWR 也支持基于角色的安全性。通過(guò) bean 的 creator 配置,可以指定用戶訪問(wèn)特定 bean 所必須屬于的 J2EE 角色。通過(guò)部署多個(gè) URL 受保護(hù)的 DWRServlet 實(shí)例,每個(gè)實(shí)例都有自己的 dwr.xml 配置文件,也可以提供擁有不同遠(yuǎn)程功能的用戶集。

用戶購(gòu)物車(chē)的 Java 表示基于 Map。當(dāng) Item 添加到購(gòu)物車(chē)中時(shí),Item 本身作為鍵被插入 Map。 Map 中對(duì)應(yīng)的值是一個(gè) Integer,代表購(gòu)物車(chē)中指定 Item 的數(shù)量。所以 Cart.java 有一個(gè)字段 contents,聲明為 Map<Item,Integer>。

使用復(fù)雜類(lèi)型作為哈希鍵給 DWR 帶來(lái)一個(gè)問(wèn)題 —— 在 JavaScript 中,數(shù)組的鍵必須是標(biāo)量的。所以,DWR 無(wú)法轉(zhuǎn)換 contents Map。但是,對(duì)于購(gòu)物車(chē)用戶界面來(lái)說(shuō),用戶需要查看的只是每個(gè)商品的名稱(chēng)和數(shù)量。所以我向 Cart 添加了一個(gè)名為 getSimpleContents() 的方法,它接受 contents Map 并根據(jù)它構(gòu)建一個(gè)簡(jiǎn)化的 Map<String,Integer>,只代表每個(gè) Item 的名稱(chēng)和數(shù)量。這個(gè)用字符串作為鍵的 map 表示可以由 DWR 的轉(zhuǎn)換器轉(zhuǎn)換成 JavaScript。

客戶對(duì) Cart 感興趣的其他字段是 totalPrice,它代表購(gòu)物車(chē)中所有商品的金額匯總。使用 Item,我還提供了一個(gè)合成的成員叫作 formattedTotalPrice,它是金額匯總的格式化好的 String 表示。

轉(zhuǎn)換購(gòu)物車(chē)

為了不讓客戶代碼對(duì) Cart 做兩個(gè)調(diào)用(一個(gè)獲得內(nèi)容,一個(gè)獲得總價(jià)),我想把這些數(shù)據(jù)一次全都發(fā)給客戶。為了做到這一點(diǎn),我添加了一個(gè)看起來(lái)有點(diǎn)兒怪的方法,如清單 5 所示:


清單 5. Cart.getCart() 方法
                        /**                        * Returns the cart itself - for DWR                        * @return the cart                        */                        public Cart getCart() {                        return this;                        }                        

雖然這個(gè)方法在普通的 Java 代碼中可能完全是多余的(因?yàn)樵谡{(diào)用這個(gè)方法時(shí),已經(jīng)有對(duì) Cart 的引用),但它允許 DWR 客戶讓 Cart 把自己序列化成 JavaScript。

除了 getCart(),需要遠(yuǎn)程化的另一個(gè)方法是 addItemToCart()。這個(gè)方法接受目錄 Item 的 ID 的 String 表示,把這個(gè)商品添加到 Cart 中并更新總價(jià)。方法還返回 Cart,這樣客戶代碼在一個(gè)操作中就能更新 Cart 的內(nèi)容并接收購(gòu)物車(chē)的新?tīng)顟B(tài)。

清單 6 是擴(kuò)展的 dwr.xml 配置文件,包含 Cart 類(lèi)進(jìn)行遠(yuǎn)程所需要的額外配置:


清單 6. 修改過(guò)的 dwr.xml 包含了 Cart 類(lèi)
                        <!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="catalog">                        </param name="class"                        value="developerworks.ajax.store.CatalogDAO"/>                        </include method="getItem"/>                        </include method="findItems"/>                        <//create>                        </convert converter="bean"                        match="developerworks.ajax.store.Item">                        </param name="include"                        value="id,name,description,formattedPrice"/>                        <//convert>                            </create creator="new" scope="session" javascript="Cart">                        </param name="class"                        value="developerworks.ajax.store.Cart"/>                        </include method="addItemToCart"/>                        </include method="getCart"/>                        <//create>                        </convert converter="bean"                        match="developerworks.ajax.store.Cart">                        </param name="include"                        value="simpleContents,formattedTotalPrice"/>                        <//convert>                          <//allow>                        </dwr>                        

在這個(gè)版本的 dwr.xml 中,我添加了 Cartcreatorconvertor。create 元素指定應(yīng)當(dāng)把 addItemToCart()getCart() 方法遠(yuǎn)程化,而且重要的是,生成的 Cart 實(shí)例應(yīng)當(dāng)放在用戶的會(huì)話中。所以,購(gòu)物車(chē)的內(nèi)容在用戶的請(qǐng)求之間會(huì)保留。

Cartconvert 元素是必需的,因?yàn)檫h(yuǎn)程的 Cart 方法返回的是 Cart 本身。在這里我指定在 Cart 的序列化 JavaScript 形式中應(yīng)當(dāng)存在的成員是 simpleContents 這個(gè)圖和 formattedTotalPrice 這個(gè)字符串。

如果對(duì)這覺(jué)得有點(diǎn)兒不明白,那么只要記住 create 元素指定的是 DWR 客戶可以調(diào)用的 Cart 服務(wù)器端方法,而 convert 元素指定在 Cart 的 JavaScript 序列化形式中包含的成員。

現(xiàn)在可以實(shí)現(xiàn)調(diào)用 Cart 的遠(yuǎn)程方法的客戶端代碼了。





回頁(yè)首


調(diào)用遠(yuǎn)程的 Cart 方法

首先,當(dāng)商店的 Web 頁(yè)首次裝入時(shí),我想檢查保存在會(huì)話中的 Cart 的狀態(tài),看是否已經(jīng)有一個(gè)購(gòu)物車(chē)了。這是必需的,因?yàn)橛脩艨赡芤呀?jīng)向 Cart 中添加了商品,然后刷新了頁(yè)面或者導(dǎo)航到其他地方之后又返回來(lái)。在這些情況下,重新載入的頁(yè)面需要用會(huì)話中的 Cart 數(shù)據(jù)對(duì)自己進(jìn)行同步。我可以在頁(yè)面的 onload 函數(shù)中用一個(gè)調(diào)用做到這一點(diǎn),就像這樣:Cart.getCart(displayCart)。請(qǐng)注意 displayCart() 是一個(gè)回調(diào)函數(shù),由服務(wù)器返回的 Cart 響應(yīng)數(shù)據(jù)調(diào)用。

如果 Cart 已經(jīng)在會(huì)話中,那么creator 會(huì)檢索它并調(diào)用它的 getCart() 方法。如果會(huì)話中沒(méi)有 Cart,那么 creator 會(huì)實(shí)例化一個(gè)新的,把它放在會(huì)話中,并調(diào)用 getCart() 方法。

清單 7 顯示了 addToCartButtonHandler() 函數(shù)的實(shí)現(xiàn),當(dāng)點(diǎn)擊商品的 Add to Cart 按鈕時(shí)會(huì)調(diào)用這個(gè)函數(shù):


清單 7. addToCartButtonHandler() 實(shí)現(xiàn)
                        /*                        * Handles a click on an Item‘s "Add to Cart" button                        */                        function addToCartButtonHandler() {                        // ‘this‘ is the button that was clicked.                        // Obtain the item ID that was set on it, and                        // add to the cart.                        Cart.addItemToCart(this.itemId,displayCart);                        }                        

由 DWR 負(fù)責(zé)所有通信,所以客戶上的添加到購(gòu)物車(chē)行為就是一個(gè)函數(shù)。清單 8 顯示了這個(gè)示例的最后一部分 —— displayCart() 回調(diào)的實(shí)現(xiàn),它用 Cart 的狀態(tài)更新用戶界面:


清單 8. displayCart() 實(shí)現(xiàn)
                        /*                        * Displays the contents of the user‘s shopping cart                        */                        function displayCart(cart) {                        // Clear existing content of cart UI                        var contentsUL = $("contents");                        contentsUL.innerHTML="";                        // Loop over cart items                        for (var item in cart.simpleContents) {                        // Add a list element with the name and quantity of item                        var li = document.createElement("li");                        li.appendChild(document.createTextNode(                        cart.simpleContents[item] + " x " + item                        ));                        contentsUL.appendChild(li);                        }                        // Update cart total                        var totalSpan = $("totalprice");                        totalSpan.innerHTML = cart.formattedTotalPrice;                        }                        

在這里重要的是要記住,simpleContents 是一個(gè)把 String 映射到數(shù)字的 JavaScript 數(shù)組。每個(gè)字符串都是一個(gè)商品的名稱(chēng),關(guān)聯(lián)數(shù)組中的對(duì)應(yīng)數(shù)字就是購(gòu)物車(chē)中該商品的數(shù)量。所以表達(dá)式 cart.simpleContents[item] + " x " + item 可能就會(huì)計(jì)算出 “2 x Oolong 128MB CF Card” 這樣的結(jié)果。

DWR 商店應(yīng)用程序

圖 3 顯示了這個(gè)基于 DWR 的 Ajax 應(yīng)用程序的使用情況:顯示了通過(guò)搜索檢索到的商品,并在右側(cè)顯示用戶的購(gòu)物車(chē):


圖 3. 基于 DWR 的 Ajax 商店應(yīng)用程序的使用情況





回頁(yè)首


DWR 的利弊

調(diào)用批處理

在 DWR 中,可以在一個(gè) HTTP 請(qǐng)求中向服務(wù)器發(fā)送多個(gè)遠(yuǎn)程調(diào)用。調(diào)用 DWREngine.beginBatch() 告訴 DWR 不要直接分派后續(xù)的遠(yuǎn)程調(diào)用,而是把它們組合到一個(gè)批請(qǐng)求中。DWREngine.endBatch() 調(diào)用則把批請(qǐng)求發(fā)送到服務(wù)器。遠(yuǎn)程調(diào)用在服務(wù)器端順序執(zhí)行,然后調(diào)用每個(gè) JavaScript 回調(diào)。

批處理在兩方面有助于降低延遲:第一,避免了為每個(gè)調(diào)用創(chuàng)建 XMLHttpRequest 對(duì)象并建立相關(guān)的 HTTP 連接的開(kāi)銷(xiāo)。第二,在生產(chǎn)環(huán)境中,Web 服務(wù)器不必處理過(guò)多的并發(fā) HTTP 請(qǐng)求,改進(jìn)了響應(yīng)時(shí)間。

現(xiàn)在可以看出用 DWR 實(shí)現(xiàn)由 Java 支持的 Ajax 應(yīng)用程序有多么容易了。雖然示例場(chǎng)景很簡(jiǎn)單,我實(shí)現(xiàn)用例的手段也盡可能少,但是不應(yīng)因此而低估 DWR 引擎相對(duì)于自己設(shè)計(jì) Ajax 應(yīng)用程序可以節(jié)約的工作量。在前一篇文章中,我介紹了手工設(shè)計(jì) Ajax 請(qǐng)求和響應(yīng)、把 Java 對(duì)象圖轉(zhuǎn)化成 JSON 表示的全部步驟,在這篇文章中,DWR 替我做了所有這些工作。我只編寫(xiě)了不到 50 行 JavaScript 就實(shí)現(xiàn)了客戶機(jī),而在服務(wù)器端,我需要做的所有工作就是給常規(guī)的 JavaBean 加上一些額外方法。

當(dāng)然,每種技術(shù)都有它的不足。同任何 RPC 機(jī)制一樣,在 DWR 中,可能很容易忘記對(duì)于遠(yuǎn)程對(duì)象進(jìn)行的每個(gè)調(diào)用都要比本地函數(shù)調(diào)用昂貴得多。DWR 在隱藏 Ajax 的機(jī)械性方面做得很好,但是重要的是要記住網(wǎng)絡(luò)并不是透明的 —— 進(jìn)行 DWR 調(diào)用會(huì)有延遲,所以應(yīng)用程序的架構(gòu)應(yīng)當(dāng)讓遠(yuǎn)程方法的粒度比較粗。正是為了這個(gè)目的,addItemToCart() 才返回 Cart 本身。雖然讓 addItemToCart() 作為一個(gè) void 方法可能更自然,但是這樣的話對(duì)它的每個(gè) DWR 調(diào)用后面都必須跟著一個(gè) getCart() 調(diào)用以檢索修改后的 Cart 狀態(tài)。

對(duì)于延遲,DWR 在調(diào)用的批處理中有自己的解決方案(請(qǐng)參閱側(cè)欄的 調(diào)用批處理)。如果不能為應(yīng)用程序提供適當(dāng)粗粒度的 Ajax 接口,那么只要有可能把多個(gè)遠(yuǎn)程調(diào)用組合到一個(gè) HTTP 請(qǐng)求中,就請(qǐng)使用調(diào)用批處理。

分離的問(wèn)題

從實(shí)質(zhì)上看,DWR 在客戶端和服務(wù)器端代碼間形成了緊密的耦合,這有許多含義:首先,遠(yuǎn)程方法 API 的變化需要在 DWR 存根調(diào)用的 JavaScript 上反映出來(lái)。第二(也是最明顯的),這種耦合會(huì)造成對(duì)客戶端的考慮會(huì)滲入服務(wù)器端代碼。例如,因?yàn)椴皇撬?Java 類(lèi)型都能轉(zhuǎn)化成 JavaScript,所以有時(shí)有必要給 Java 對(duì)象添加額外方法,好讓它能夠更容易地遠(yuǎn)程化。在示例場(chǎng)景中,我通過(guò)把 getSimpleContents() 方法添加到 Cart 來(lái)解決這個(gè)問(wèn)題。我還添加了 getCart() 方法,它在 DWR 場(chǎng)景中是有用的,但在其他場(chǎng)景中則完全是多余的。由于遠(yuǎn)程對(duì)象粗粒度 API 的需要以及把某些 Java 類(lèi)型轉(zhuǎn)化成 JavaScript 的問(wèn)題,所以可以看到遠(yuǎn)程 JavaBean 會(huì)被那些只對(duì) Ajax 客戶有用的方法“污染”。

為了克服這個(gè)問(wèn)題,可以使用包裝器類(lèi)把額外的特定于 DWR 的方法添加到普通 JavaBean。這意味著 JavaBean 類(lèi)的 Java 客戶可能看不到與遠(yuǎn)程相關(guān)聯(lián)的額外的毛病,而且也允許給遠(yuǎn)程方法提供更友好的名稱(chēng) —— 例如用 getPrice() 代替 getFormattedPrice()。圖 4 顯示的 RemoteCart 類(lèi)對(duì) Cart 進(jìn)行了包裝,添加了額外的 DWR 功能:


圖 4. RemoteCart 為遠(yuǎn)程功能對(duì) Cart 做了包裝

最后,需要記?。篋WR Ajax 調(diào)用是異步的,所以不要期望它們會(huì)按照分派的順序返回。在示例代碼中我忽略了這個(gè)小問(wèn)題,但是在這個(gè)系列的第一篇文章中,我演示了如何為響應(yīng)加時(shí)間戳,以此作為保證數(shù)據(jù)到達(dá)順序的一種簡(jiǎn)單手段。





回頁(yè)首


結(jié)束語(yǔ)

正如所看到的,DWR 提供了許多東西 —— 它允許迅速而簡(jiǎn)單地創(chuàng)建到服務(wù)器端域?qū)ο蟮?Ajax 接口,而不需要編寫(xiě)任何 servlet 代碼、對(duì)象序列化代碼或客戶端 XMLHttpRequest 代碼。使用 DWR 部署到 Web 應(yīng)用程序極為簡(jiǎn)單,而且 DWR 的安全性特性可以與 J2EE 基于角色的驗(yàn)證系統(tǒng)集成。但是 DWR 并不是對(duì)于任何一種應(yīng)用程序架構(gòu)都適合,所以在設(shè)計(jì)域?qū)ο蟮?API 時(shí)需要做些考慮。

如果想學(xué)習(xí)用 DWR 進(jìn)行 Ajax 的利弊的更多內(nèi)容,最好的方式就是下載并開(kāi)始實(shí)踐。DWR 有許多我沒(méi)有介紹的特性, 文章源代碼 是把 DWR 投入使用的一個(gè)良好起點(diǎn)。請(qǐng)參閱 參考資料,學(xué)習(xí)關(guān)于 Ajax、DWR 和相關(guān)技術(shù)的更多內(nèi)容。

這個(gè)系列中要指出的最重要的一點(diǎn)是:對(duì)于 Ajax 應(yīng)用程序,沒(méi)有包治百病的解決方案。Ajax 是一個(gè)快速發(fā)展的領(lǐng)域,不斷有新技術(shù)涌現(xiàn)。在這個(gè)系列的三篇文章中,我的重點(diǎn)在于帶您開(kāi)始在 Ajax 應(yīng)用程序的 Web 層中利用 Java 技術(shù) —— 不管是選擇基于 XMLHttpRequest 的帶有對(duì)象序列化框架的技術(shù),還是選擇 DWR 這樣的更高級(jí)抽象。請(qǐng)?jiān)诤罄m(xù)幾個(gè)月中留意面向 Java 開(kāi)發(fā)人員介紹 Ajax 的文章。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
面向Java開(kāi)發(fā)人員的Ajax技術(shù)
面向 Java 開(kāi)發(fā)人員的 Ajax: 構(gòu)建動(dòng)態(tài)的 Java 應(yīng)用程序
DWR技術(shù)
服務(wù)器推送技術(shù) - ServerPush
AJAX made simple with DWR
Ajax 框架
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服