級別: 中級 Roland Barcia (barcia@us.ibm.com), IT 咨詢專家, IBM WebSphere 軟件服務(wù)部
2006 年 8 月 21 日 了 解如何使用 Dojo 工具包為 Java? Platform Extended Edition (Java EE) 應(yīng)用程序構(gòu)建企業(yè) SOA 客戶端,以及如何使用 JavaScript Object Notation–RPC (JSON-RPC) 來調(diào)用服務(wù)器端 Java 對象。 引言 異 步 JavaScript 和 XML (Ajax) 是使用本機瀏覽器技術(shù)構(gòu)建富 Web 應(yīng)用程序的新方法。對于編寫需要某些類型的“活動”用戶界面的復(fù)雜應(yīng)用程序的開發(fā)人員,JavaScript 在這方面已經(jīng)做得很好。不過,JavaScript 難于編碼、調(diào)試、移植和維護。使用 Ajax 工具包有助于最大程度地減少使用 JavaScript 和 Ajax 帶來的許多常見問題。優(yōu)秀的 Ajax 工具包提供了一組可重用的小部件、用于擴展和創(chuàng)建小部件的框架、事件系統(tǒng)、JavaScript 實用工具和增強的異步服務(wù)器調(diào)用支持。在本文中,我們將討論如何使用 Dojo 工具包為 Java EE 應(yīng)用程序構(gòu)建企業(yè) SOA 客戶端。我們還將使用 JSON (JavaScript Object Notation)–RPC 來調(diào)用服務(wù)器端 Java 對象。 在本文中,我們還將向您提供以下內(nèi)容的簡要說明:Ajax、Dojo、JSON 和 JSON-RPC,以及一些設(shè)計 Ajax 應(yīng)用程序的設(shè)計原則和您可以下載并親自嘗試運行的簡短示例。
Ajax 概述 有許多關(guān)于 Ajax 的論文、文章和書籍。我不打算對 Ajax 進行深入介紹。有關(guān)詳細信息,請查閱參考資料。 Ajax 可作為使用本機瀏覽器組件構(gòu)建網(wǎng)站的體系結(jié)構(gòu)樣式。Ajax 的關(guān)鍵部分有: - JavaScript,它可以編排頁面元素,從而獲得最佳 Ajax 用戶體驗。
- Cascading Style Sheets (CSS),它可以定義頁面元素的可視樣式。
- 文檔對象模型(Document Object Model,DOM),它將網(wǎng)頁結(jié)構(gòu)作為一組可以使用 JavaScript 操作的可編程對象提供。
- XMLHttpRequest,它支持以后臺活動的形式從 Web 資源檢索數(shù)據(jù)。
XMLHttpRequest 對象是關(guān)鍵部分。 XMLHttpRequest 對象 XMLHttpRequest 對象是 Ajax 用于進行異步請求的機制。圖 1 說明了該流程: 圖 1. XMLHttpRequest 對象進行異步請求的流程圖 XMLHttpRequest 對象是瀏覽器中提供的 JavaScript 對象。(Microsoft? 和 Mozilla 瀏覽器各有自已的版本)。該流程如下所示: - 頁面調(diào)用某個 JavaScript。
- JavaScript 函數(shù)創(chuàng)建 XMLHttpRequest 對象。這包括設(shè)置要調(diào)用的 URL 和 HTTP 請求參數(shù)。
- JavaScript 函數(shù)注冊回調(diào)處理程序。HTTP 響應(yīng)調(diào)用此回調(diào)處理程序。
- JavaScript 函數(shù)調(diào)用
XMLHttpRequest 對象上的 send 方法,該方法接著將 HTTP 請求發(fā)送到服務(wù)器。 XMLHttpRequest 對象立即將控制返回到 JavaScript 方法。此時,用戶可以繼續(xù)使用該頁面。 - 稍后,HTTP 服務(wù)器通過調(diào)用回調(diào)處理程序返回 HTTP 響應(yīng)。
- 回調(diào)處理程序可以訪問 HTML DOM 對象。它可以動態(tài)更新頁面元素,而無需中斷用戶(除非您碰巧更新用戶正在使用的 DOM 對象)。
通過異步更新頁面的 DOM,還可以在本地進行異步請求。
Dojo 工具包概述 Dojo 使您能夠方便地構(gòu)建動態(tài)站點。它提供一個豐富的小部件庫,您可以使用它組成頁面。您可以使用基于 Dojo 方面的事件系統(tǒng)將事件附加到組件,以創(chuàng)建豐富的交互體驗。此外,您可以使用幾個 Dojo 庫進行異步服務(wù)器請求、添加動畫效果和瀏覽存儲實用工具等等。 Dojo 小部件 Dojo 提供了您可以用于構(gòu)建頁面的一組豐富的小部件。您可以使用多個方法創(chuàng)建 Dojo 小部件。Dojo 的眾多優(yōu)點之一是它允許您使用標準的 HTML 標記。然后,可以將這些標記用于小部件。這樣,HTML 開發(fā)人員就可以方便地使用 Dojo,如清單 1 所示: 清單 1. 在 HTML 標記中使用 Dojo <div dojoType="FloatingPane" class="stockPane" title="Stock Form" id="pane" constrainToContainer="true" displayMaximizeAction="true"> <h2>Stock Service</h2> Enter symbol: <input dojoType="ValidationTextBox" required="true" id="stockInput"> <p /> <button dojoType="Button2" widgetId="stockButton"> Get Stock Data </button> <div id="resultArea" /> </div> | 您可以使用 div 標記來定義小部件的位置,而在頁面加載或?qū)κ录M行響應(yīng)時 Dojo 可以在這些地方放置小部件。您還可以使用更具體的標記,如 <dojo:widget> ,并向其中添加 Dojo 小部件屬性。在清單 1 中,我們將 dojoType 屬性添加到 button 標記。在設(shè)置了標記之后,您需要在一些 JavaScript 內(nèi)部加載小部件,如清單 2 所示。您可以將標記嵌入到頁面內(nèi)部,但是我們建議將其放置在單獨的 JS 文件中。在本文的稍后部分中,我們將闡明一些 MVC 設(shè)計原則。 清單 2. 在 HTML 標記中使用 Dojo //require statements dojo.require("dojo.widget.*" ); dojo.require("dojo.event.*"); dojo.require("dojo.widget.Button2"); dojo.require("dojo.widget.FloatingPane" ); //all dojo.require above this line dojo.hostenv.writeIncludes(); dojo.require(); | 您可以在 JavaScript 中創(chuàng)建、訪問、修改和刪除小部件,從而實現(xiàn)動態(tài)行為。在我們的示例中,您將看到在 JavaScript 中訪問小部件的示例。 Dojo 事件系統(tǒng) Dojo 事件系統(tǒng)使用面向方面的技術(shù)將事件附加到小部件。這可以將小部件與實際的事件處理分離。Dojo 不是將硬代碼 JavaScript 事件添加到 html 標記上,而是提供允許您將事件附加到小部件的 API,如清單 3 所示。 清單 3. 使用 Dojo 將事件處理程序附加到小部件 function submitStock() { ... } function init() { var stockButton = dojo.widget.byId(‘stockButton‘); dojo.event.connect(stockButton, ‘onClick‘, ‘submitStock‘); } dojo.addOnLoad(init); | 通過使用 connect 方法,您可將 JavaScript 方法連接到小部件。您還可以在 div 節(jié)點上附加 dojoAttachEvent ,如下所示。某些 HTML 標記沒有定義事件,所以這是一個方便的擴展。 清單 4. 使用 Dojo 將事件附加到 HTML 標記 <div dojoAttachPoint="divNode" dojoAttachEvent="onClick; onMouseOver: onFoo;"> | Dojo 事件系統(tǒng)還允許多個高級功能,如: - 聲明在現(xiàn)有的事件處理程序之前或之后插入事件處理程序的建議。
- 允許小部件在瀏覽器中訂閱或發(fā)布主題。
- 添加事件回調(diào)。
- 可用于表示事件的
event 規(guī)范化對象。 有關(guān)詳細信息,請參見 http://dojo.jot.com/EventExamples。 Dojo 異步服務(wù)器請求 Dojo 通過抽象特定于瀏覽器的詳細信息,提供了對服務(wù)器進行異步請求的簡單方法。Dojo 允許您創(chuàng)建數(shù)據(jù)結(jié)構(gòu)來隱藏詳細信息,如清單 5 所示。 清單 5. 使用 Dojo 進行異步請求 var bindArgs = { url: "/DojoJ2EE/MyURI", mimetype: "text/javascript", error: function(type, errObj){ // handle error here }, load: function(type, data, evt){ // handle successful response here } }; // dispatch the request var requestObj = dojo.io.bind(bindArgs); | 此外,Dojo 使用 JSON-RPC 標準支持 RPC。在接下來的部分中,我們將看一看 Dojo 對 JSON 的支持。 附加的 Dojo 功能 Dojo 是一個具有許多功能的豐富庫,包括: - 處理 html、字符串、樣式、dom、正則表達式和若干其他實用工具的通用庫。
- 包括字典、ArraryLists、隊列、SortedList、設(shè)置和堆棧的數(shù)據(jù)結(jié)構(gòu)。
- 用于添加動畫效果、驗證、拖放和若干其他功能的可視化 Web 實用工具。
- 數(shù)學(xué)和加密庫。
- 存儲組件。
- XML 解析
有關(guān)詳細信息,請參見 http://manual.dojotoolkit.org/index.html。
JSON 概述 JSON 是 JavaScript 的對象文字符號的子集,它是在 JavaScript 中表示數(shù)據(jù)結(jié)構(gòu)的常用方法。JSON 是一種完全與語言無關(guān)的文本格式,但使用編程人員熟悉的與 C 語言家族(包括 C、C++、C#、Java、JavaScript、Perl、Python 和許多其他語言)類似的約定。這些屬性使 JSON 成為 Ajax 客戶端的理想數(shù)據(jù)交換語言。 JSON 構(gòu)建在兩種結(jié)構(gòu)的基礎(chǔ)上: - 名稱/值對的集合。在不同的語言中,它被實現(xiàn)為對象、記錄、結(jié)構(gòu)、字典、哈希表、有鍵列表或者關(guān)聯(lián)數(shù)組。
- 值的有序列表。在大多數(shù)語言中,它被實現(xiàn)為數(shù)組、向量、列表或序列。
JSON 對象的示例如下: var myJSONObject = {"id": 4, "name": "Roland Barcia", "pets": ["dog","cat","fish"]}; | 在示例中,我們對值對進行了命名。括號中的任何內(nèi)容都是一個列表。在不同的編程語言中都有一組豐富的實現(xiàn)。有關(guān)詳細信息,請參見 http://json.org/。 JSON-RPC JSON-RPC 是一種輕量級遠程過程調(diào)用協(xié)議,在此協(xié)議中,JSON 可以連續(xù)請求和響應(yīng)。向遠程服務(wù)發(fā)送請求可以調(diào)用遠程方法。該請求是具有三個屬性的單個對象: -
method - 包含要調(diào)用的方法名稱的字符串。 -
params - 作為參數(shù)傳遞到方法的對象數(shù)組。 -
id - 請求 ID。它可以屬于任何類型。它用于將響應(yīng)與其應(yīng)答的請求相匹配。 當方法調(diào)用完成時,服務(wù)必須對響應(yīng)進行應(yīng)答。此響應(yīng)是具有三個屬性的單個對象: -
result - 被調(diào)用方法返回的對象。它必須為 null ,以避免在調(diào)用該方法時發(fā)生錯誤。 -
error - error 對象(如果在調(diào)用方法時發(fā)生錯誤)。它必須為 null (如果不存在任何錯誤)。 -
id - 它必須是與響應(yīng)的請求相同的 ID。 通知是沒有響應(yīng)的特殊請求。它與帶有一個異常的請求對象具有相同的屬性: JSON 與 XML XML 是一種用于面向服務(wù)的體系結(jié)構(gòu) (SOA) 和數(shù)據(jù)傳輸?shù)某R妭鬏?。當然,目前許多服務(wù)以 SOAP 格式存在。不過,何時將其用于數(shù)據(jù)傳輸在 Ajax 社區(qū)中存在分岐。JSON 有以下幾個優(yōu)點: - 瀏覽器解析 JSON 的速度比 XML 快。
- JSON 構(gòu)造是友好的編程語言,并容易轉(zhuǎn)換為后端編程語言(如 Java)。
- JSON 相當穩(wěn)定。JSON 的附加內(nèi)容將成為超集。
XML 有以下優(yōu)點: - 調(diào)用將 XML 用作傳輸?shù)默F(xiàn)有服務(wù)。
- 使用 XSLT 可以動態(tài)轉(zhuǎn)換 XML。這是企業(yè)服務(wù)總線 (ESB) 方案中的理想功能。
用于 Dojo 的 JSON-RPC Dojo 為調(diào)用 JSON-RPC 請求提供了抽象層。用于 Dojo 的 JSON-RPC 引入了標準方法描述(Standard Method Description,SMD)的概念。SMD 文件是 JSON-RPC 服務(wù)的描述。它允許您以中立方式描述 JSON 調(diào)用。清單 6 提供了此類 JSON 調(diào)用的示例: 清單 6. Dojo 中的 SON 調(diào)用 {"SMDVersion":".1", "objectName":"StockService", "serviceType":"JSON-RPC", "serviceURL":"/DojoJ2EE/JSON-RPC", "methods":[ {"name":"getStockData", "parameters":[ {"name":"symbol", "type":"STRING"} ] } ] } | 您可以使用 Dojo API 調(diào)用服務(wù): var StockService = new dojo.rpc.JsonService("/path/to/StockService.smd"); StockService.getStockData("IBM",stockResultCallback); 這將通過網(wǎng)絡(luò)發(fā)送此結(jié)構(gòu): {"id": 2, "method": "getStockData", "params": ["IBM"]} JSON-RPC Java ORB JSON-RPC 為遠程過程調(diào)用定義標準格式,但是不存在對后端技術(shù)的標準映射。JSON-RPC Java Orb 提供了這樣一種機制:注冊 Java 對象,并將它們公開為 JSON-PRC 服務(wù)。它還在 JavaScript 中提供客戶端 API,以調(diào)用服務(wù)。 如果您選擇使用 Dojo,則可以編寫自已的綁定代碼。用于 Java 的 JSON API 可以提供幫助。有關(guān)詳細信息,請參見 (http://developer.berlios.de/projects/jsontools/)。在我們的示例中,我們將使用 JSON-RPC Java ORB 進行異步請求,以利用服務(wù)器端綁定代碼。 JSON-RPC Java Orb 允許您在一種 Servlet 范圍(請求、會話、應(yīng)用程序)內(nèi)注冊 Java 對象。然后,它可以使用 JSON-RPC 請求來調(diào)用 Java 對象。為此,可以將對象類型放在 JSON 對象之前。由于 Dojo API 不執(zhí)行此操作,所以用于 JSON-RPC 的 Dojo 客戶端 API 與用于 Java 的 JSON-RPC 不兼容。 清單 7 提供了如何向 HttpSession 注冊 Java 對象的示例: 清單 7. 注冊 Java 對象的 HttpSession HttpSession session = sessionEvent.getSession(); JSONRPCBridge json_bridge = null; json_bridge = (JSONRPCBridge) session.getAttribute("JSONRPCBridge"); if(json_bridge == null) { json_bridge = new JSONRPCBridge(); session.setAttribute("JSONRPCBridge", json_bridge); } json_bridge.registerObject ("StockService",StockServiceImpl.getStockService()); | 您可以在 Servlet 或 HttpListener 中執(zhí)行此操作。然后將 JavaScript 客戶端寫入到 Java 服務(wù),如清單 8 所示。 清單 8. 連接 Java 服務(wù)的 JSONRpcClient jsonrpc = new JSONRpcClient("/DojoJ2EE/JSON-RPC"); var stockButton = dojo.byId(‘stockInput‘); jsonrpc.StockService.getStockData(stockResultCallBack,stockButton.value); | 此請求會發(fā)送以下有效負載: {"id": 2, "method": "StockService.getStockData", "params": ["IBM"]} 響應(yīng)與以下所示類似: {"result":{"javaClass":"com.ibm.issw.json.service.StockData","price":100, "companyName":"International Business Machine","symbol":"IBM"},"id":2} 用于 Java 客戶端的 JSON-RPC 將處理此響應(yīng),并向您提供一個靜態(tài)接口。 為了支持請求,您需要注冊特殊的 Servlet。稍后,我將向您介紹如何執(zhí)行此操作。 JSON-RPC Java ORB 的一個缺點是只有單個 URL,這導(dǎo)致使用 Java EE 安全來保護 URL 非常困難。作為一種變通方法,您可以在 HTTP 會話層注冊服務(wù),并根據(jù)安全需要添加它們。
為企業(yè)應(yīng)用程序構(gòu)建企業(yè)客戶端 在此部分中,我將討論一些設(shè)計原則,然后詳細講解一個示例。您可以下載完整的 WAR 文件,并親自嘗試該應(yīng)用程序。 模型-視圖-控制器 在 Java EE 領(lǐng)域中,模型-視圖-控制器 (MVC) 已經(jīng)變得非常成熟,這歸功于 Struts 和 JavaServer Faces 之類的框架。使用 Ajax 可以進行正確的 MVC 設(shè)計,這對成功的 Web 應(yīng)用程序至關(guān)重要。此外,SOA 允許直接從 Ajax 應(yīng)用程序調(diào)用服務(wù)。這樣做有幾個優(yōu)點,如 WebSphere 期刊文章 中所述。 圖 2 提供了 Ajax 客戶端和 Java EE 應(yīng)用程序之間理想生態(tài)系統(tǒng)的簡要概述。 圖 2. Ajax 客戶端和 Java EE 應(yīng)用程序之間的理想生態(tài)系統(tǒng) 在服務(wù)器端,業(yè)務(wù)和門戶功能現(xiàn)在被公開為某一類型的服務(wù)接口。在服務(wù)的形式下,應(yīng)遵循正確的服務(wù)器端最佳實踐。服務(wù)器應(yīng)用程序應(yīng)公開過程粒度服務(wù)。JavaServer Faces 之類的框架現(xiàn)在負責執(zhí)行初始呈現(xiàn);不過,對于客戶端 Ajax 工具包,這是可選的。 在瀏覽器上,分離關(guān)注的內(nèi)容非常重要。圖 3 突出顯示了 Java 服務(wù)器文件結(jié)構(gòu): 圖 3. Java 服務(wù)器文件結(jié)構(gòu) 您可以選擇每頁有一個 JavaScript 控制器。不過,對于復(fù)雜的門戶頁,您可以將相關(guān)事件分組成小型的控制器集。 控制器文件應(yīng): - 向小部件加載網(wǎng)絡(luò)請求處理程序,如圖 4 所示:
圖 4. 將請求處理程序附加到小部件
- 實現(xiàn)請求處理程序。請求處理程序不應(yīng)有太多的邏輯。它們應(yīng)委派給服務(wù) Facade,以便與后端交互。
- 實現(xiàn)回調(diào)處理程序方法。回調(diào)應(yīng)將呈現(xiàn)委派給獨立 JS 文件。此外,它們應(yīng)將存儲中間狀態(tài)的工作委派給獨立 Java 服務(wù)器文件。對于無狀態(tài)交互,可以直接將結(jié)果傳遞到 rendering.js 文件。
圖 5 說明了組件之間的流: 圖 5. 從客戶端到請求處理程序,再到回調(diào)處理程序的信息流 呈現(xiàn)文件包含呈現(xiàn)組件的邏輯或基于事件結(jié)果的用戶界面。 Business Facades 應(yīng)包含代理服務(wù)器請求的方法。DataCopy 應(yīng)維護需要本地保存在瀏覽器中的本地視圖對象。 為 Dojo 設(shè)置 Java EE 應(yīng)用程序 對于 Dojo,您必須添加 JavaScript 文件作為 Web 應(yīng)用程序的一部分。您可以將 dojo 文件夾復(fù)制到 Web 應(yīng)用程序文件夾,如圖 6 所示: 圖 6. 添加使用 Dojo 所必需的 JavaScript 文件 為 JSON-RPC Java Orb 設(shè)置 Java EE 應(yīng)用程序 為了在應(yīng)用程序中使用 JSON-RPC Java Orb,您需要在 Web 應(yīng)用程序的 lib 目錄中添加 json-rpc-1.0.jar。還需要將單個 jsonrpc.js 文件添加到 Web 內(nèi)容文件夾中,如圖 7 所示: 圖 7. 添加使用 JSON-RPC Java Orb 所必需的 JavaScript 文件 為了使 Java EE 應(yīng)用程序能夠接收用于 Java 請求的 JSON-RPC,您必須添加 JSONRPCServlet,如清單 9 所示: 清單 9. 使用 JSONRPCServlet 所需的代碼 <servlet> <servlet-name> com.metaparadigm.jsonrpc.JSONRPCServlet </servlet-name> <servlet-class> com.metaparadigm.jsonrpc.JSONRPCServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name> com.metaparadigm.jsonrpc.JSONRPCServlet </servlet-name> <url-pattern>/JSON-RPC</url-pattern> </servlet-mapping> | 對于 SOA Ajax 使 SOA 客戶端更完美。在我們的示例中,我們使用了一個簡單的 Java 服務(wù)。 Java 服務(wù) 圖 8 是基于 Java 的服務(wù)模型: 圖 8. 基于 Java 的服務(wù)模型 我們使用了一個簡單的硬編碼實現(xiàn),如清單 10 所示: 清單 10. 簡單的硬編碼 Java 服務(wù) public StockData getStockData(String symbol) throws StockException { if(symbol.equals("IBM")) { StockData stockData = new StockData(); stockData.setCompanyName("International Business Machine"); stockData.setSymbol(symbol); stockData.setPrice(100.00); return stockData; } else { throw new StockException("Symbol: " + symbol + " not found!"); } } | 使用 JSON-RPC 公開 Java 服務(wù) 為了公開 Java 應(yīng)用程序,我使用了被稱為 ExportServices 的 HttpSessionListener,以便為用戶注冊服務(wù),如清單 11 所示: 清單 11. ExportServices,即公開 Java 服務(wù)的 HttpSessionListener import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import com.ibm.issw.json.service.StockServiceImpl; import com.metaparadigm.jsonrpc.JSONRPCBridge; public class ExportServices implements HttpSessionListener { public void sessionCreated(HttpSessionEvent sessionEvent) { HttpSession session = sessionEvent.getSession(); JSONRPCBridge json_bridge = null; json_bridge = (JSONRPCBridge) session.getAttribute("JSONRPCBridge"); if(json_bridge == null) { json_bridge = new JSONRPCBridge(); session.setAttribute("JSONRPCBridge", json_bridge); } json_bridge.registerObject ("StockService",StockServiceImpl.getStockService()); } public void sessionDestroyed(HttpSessionEvent arg0) { } } | 您需要將偵聽器添加到應(yīng)用程序中(通過將其添加到 web.xml),如清單 12 所示: 清單 12. 添加到 web.xml 的 ExportServices 偵聽器 <listener> <listener-class>ExportServices</listener-class> </listener> | 客戶端開發(fā)過程 設(shè)置了基礎(chǔ)結(jié)構(gòu)并公開了服務(wù)之后,現(xiàn)在我們可以構(gòu)建 Web 客戶端了。通過 Dojo,我們利用小部件構(gòu)建網(wǎng)頁并利用事件模型。圖 9 說明了建議的開發(fā)過程: 圖 9. 開發(fā)過程示例 我將使用此過程演示該示例。 從小部件構(gòu)建 UI 首先構(gòu)建 UI。請參見清單 13,了解示例 UI。 創(chuàng)建 UI: - 加載腳本:
- dojo
- jsonrpc
- StockClientController
- resultRenderer
- 構(gòu)建頁面,并結(jié)合使用
div 和 HTML 標記以創(chuàng)建 Dojo 小部件。
清單 13. HTML UI <html> <head> <title>Stock Form</title> <script type="text/javascript" src="../dojoAjax/dojo.js"></script> <script type="text/javascript" src="../jsonrpc.js"></script> <script type="text/javascript" src="../view/resultRender.js"></script> <script type="text/javascript" src="../controller/StockClientController.js"></script> <link REL=StyleSheet HREF="../StockApp.css" TYPE="text/css" ></link> </head> <body> <div class="layout" dojoType="LayoutContainer"> <div dojoType="ContentPane" class="stockContent" layoutAlign="bottom" id="docpane" isContainer="true" executeScripts="true"> <div dojoType="FloatingPane" class="stockPane" title="Stock Form" id="pane" constrainToContainer="true" displayMaximizeAction="true"> <h2>Stock Service</h2> Enter symbol: <input dojoType="ValidationTextBox" required="true" id="stockInput"> <p /> <button dojoType="Button2" widgetId="stockButton"> Get Stock Data </button> <div id="resultArea" /> </div> </div> </div> </body> </html> | - StockClientController.js 非常關(guān)鍵。在腳本的開頭,使用
dojo.require 方法加載所需的小部件,然后初始化 Dojo 環(huán)境,如清單 14 所示。
清單 14. 初始化 Dojo 環(huán)境的 StockClientController //require statements dojo.require("dojo.widget.*" ); dojo.require("dojo.event.*"); dojo.require("dojo.widget.Button2"); dojo.require("dojo.widget.FloatingPane" ); //all dojo.require above this line dojo.hostenv.writeIncludes(); dojo.require(); | 操作前后需要考慮的事項 在 Ajax 中,需要考慮的一件事是,在觸發(fā)事件之前,不要顯示某些用戶界面。不過,一種做法是放置 div 標記作為占位符。然后,可以使用 DOM 或 Dojo API 訪問此區(qū)域,并添加動態(tài) UI 元素。在我們的應(yīng)用程序中,我添加了一個簡單的 div ,以獲得以下結(jié)果: <div id="resultArea" /> 附加樣式表 接下來,使用 CSS 添加樣式。CSS 是設(shè)置 Ajax 應(yīng)用程序格式的標準方法。使用 CSS,您可以將樣式定義應(yīng)用于多個 div 標記,方法是將標記的 class 屬性設(shè)置為該樣式的名稱。這允許您重用樣式定義。清單 15 顯示了我使用的樣式表: 清單 15. 在 UI 中使用的樣式表 @CHARSET "ISO-8859-1"; .layout { width: 100%; height: 80%; } .stockContent { width: 100%; height: 90%; background-color: #F0F0F0 ; padding: 10px; } .stockPane { width: 40%; height: 250px; } .exceptionMsg { color: #FF0000; } | 服務(wù)視圖 接 下來,一個好的想法是確保 UI 開發(fā)人員在 JavaScript 中擁有一個服務(wù)視圖。Dojo 使用 SMD 來做到這一點,如前面所述。用于 Java 的 JSON-RPC 為我們提供了直接從 JavaScript 調(diào)用 Java 服務(wù)的能力,如清單 16 所示: 清單 16. 直接調(diào)用 Java 服務(wù)的 JavaScript <script type="text/javascript" src="../jsonrpc.js"></script> jsonrpc.StockService.getStockData(stockResultCallBack,stockButton.value); | 構(gòu)建請求事件處理程序 接著,在控制器 JS 文件中,我們需要創(chuàng)建事件處理程序和回調(diào)處理程序?;卣{(diào)處理程序應(yīng)是其他工作的 Facade。在我們的示例中,事件處理程序?qū)惒秸{(diào)用我們的服務(wù),并將回調(diào)傳遞到相應(yīng)的方法。XMLHttpRequest 對象的此抽象由 JSON-RPC-Java 提供。在接收到響應(yīng)時,回調(diào)委派給呈現(xiàn),如清單 17 所示: 清單 17. 控制器文件中的回調(diào)和事件處理程序 function stockResultCallBack(result,exception) { try { renderStockResult(result,exception); } catch(e) { alert(e); } } function submitStock() { try { jsonrpc = new JSONRpcClient("/DojoJ2EE/JSON-RPC"); var stockButton = dojo.byId(‘stockInput‘); jsonrpc.StockService. getStockData(stockResultCallBack,stockButton.value); } catch(e) { alert(e); } } | 在加載時加載初始 UI 和網(wǎng)絡(luò)請求事件 下面,我們在頁面初始化時使用 Dojo 這種有力的工具,將小部件連接到請求處理程序。請參見清單 18 中的 init 方法。dojo.addOnLoad() 方法允許您使用同一面向方面的技術(shù),將 init 方法附加到頁面加載事件。 清單 18. init() 方法 function init() { var stockButton = dojo.widget.byId(‘stockButton‘); dojo.event.connect(stockButton, ‘onClick‘, ‘submitStock‘); } dojo.addOnLoad(init); | 呈現(xiàn)響應(yīng) 最 后一步是添加動態(tài)呈現(xiàn)響應(yīng)代碼。我將它放置在獨立呈現(xiàn)器 JS 文件中。您可以使用各種方法來呈現(xiàn)響應(yīng)。在我們的示例中,我們將結(jié)合使用 DOM API 和 Dojo 實用工具來構(gòu)建簡單的表。在這里,我們可以使用 Dojo 的小部件之一,但是我希望對清單 19 中的函數(shù) renderStockResult 使用自已的代碼,以便突出顯示一些 Dojo 實用工具和數(shù)據(jù)結(jié)構(gòu)。 要創(chuàng)建呈現(xiàn)響應(yīng)代碼,請執(zhí)行下列操作: - 在
renderStockResult 函數(shù)中,使用 dojo.byId() 方法訪問 resultArea 對象。 - 檢查任何異常;如果
renderStockResult 含有傳遞給它的異常,它會將該異常傳遞到錯誤處理程序并返回。 - 使用
Dictionary (類似于 Java HashMap )和 ArrayList Dojo 結(jié)構(gòu)來存放 result 數(shù)據(jù)。 - 將結(jié)構(gòu)化數(shù)據(jù)傳遞到通用表創(chuàng)建者方法。
清單 19. 呈現(xiàn)響應(yīng)方法 dojo.require("dojo.collections.*"); dojo.require("dojo.fx.*"); function renderStockResult(result,exception) { var resultArea = dojo.byId(‘resultArea‘); if(exception) { handleStockError(resultArea,exception); return; } var symbolHeader = "Symbol:"; var priceHeader = "Price:"; var companyNameHeader = "Company Name:"; var headers = new dojo.collections.ArrayList(); headers.add(symbolHeader); headers.add(priceHeader); headers.add(companyNameHeader); var column = new dojo.collections.Dictionary(); column.add(symbolHeader,result.symbol); column.add(priceHeader,result.price); column.add(companyNameHeader,result.companyName); var data = new dojo.collections.ArrayList(); data.add(column); createTableWithVerticleHeading(resultArea,headers,data); } | 設(shè)置了數(shù)據(jù)結(jié)構(gòu)之后,調(diào)用具體的 createTableWithVerticleHeading 方法。實際上,此類實用工具將會被外部化。在下面顯示的方法中,我們將使用 Dojo Iterator 對象來遍歷這些數(shù)據(jù)結(jié)構(gòu)并創(chuàng)建表。我要在下面指出的另一個方法是 dojo.fx.html.fadeShow(table, 200) ,您可以使用該方法將淡入效果添加到結(jié)果的打印中。這只是某些動畫的一瞬。在清單 20 中,Dojo 代碼為粗體。 清單 20. 表創(chuàng)建方法 function createTableWithVerticleHeading(root,headers,data) { var oldResult = dojo.byId(‘resultTable‘); if(oldResult) { root.removeChild(oldResult); } var exceptionMsg = dojo.byId(‘stockExceptionMsg‘); if(exceptionMsg) { resultArea.removeChild(exceptionMsg); } var table = document.createElement("table"); dojo.fx.html.fadeShow(table, 200); table.setAttribute("id","resultTable"); root.appendChild(table); var headerIter = headers.getIterator(); while(!headerIter.atEnd()) { var row = document.createElement("tr"); table.appendChild(row); var element = document.createElement("th"); element.setAttribute("align","left"); row.appendChild(element); var header = headerIter.get(); var dataElement = document.createTextNode(header); element.appendChild(dataElement); var dataIter = data.getIterator(); while(!dataIter.atEnd()) { var resultItem = dataIter.get(); var item = resultItem.item(header); var elementItem = document.createElement("td"); elementItem.setAttribute("align","left"); row.appendChild(elementItem); var dataText = document.createTextNode(item); elementItem.appendChild(dataText); } } } | 最后,我們將添加簡單的錯誤處理方法,以打印錯誤消息,如清單 21 所示。請記住,通過在元素上設(shè)置類屬性,然后委派給 CSS 文件,可添加粗體文本。 清單 21. 錯誤處理方法 function handleStockError(resultArea,exception) { var oldResult = dojo.byId(‘resultTable‘); if(oldResult) { resultArea.removeChild(oldResult); } var exceptionMsg = dojo.byId(‘stockExceptionMsg‘); if(exceptionMsg) { resultArea.removeChild(exceptionMsg); } var error = document.createElement("h4"); error.setAttribute("class","exceptionMsg"); error.setAttribute("id","stockExceptionMsg"); var errorText = document.createTextNode(exception.message); resultArea.appendChild(error); error.appendChild(errorText); return; } | 測試應(yīng)用程序 您可以下載應(yīng)用程序的最終 WAR 文件??蓪⑵浒惭b在任何應(yīng)用服務(wù)器(如 WebSphere Application Server)上。部署了應(yīng)用程序后,您可以打開 HTML 頁,以查看浮點形式,如圖 10 所示: 圖 10. HTML 中的浮點形式 可以自由移動該形式。您可能注意到,我已將浮點綁定到外部容器,如圖 11 所示: 圖 11. 綁定到外部容器的浮點 輸入 IBM 以調(diào)用 Java EE 應(yīng)用程序。結(jié)果應(yīng)類似于圖 12 所示的形式: 圖 12. 將 IBM 輸入到應(yīng)用程序的結(jié)果 接下來,輸入一些其他數(shù)據(jù),以測試錯誤處理程序,如圖 13 所示。 圖 13. 測試錯誤處理程序 Dojo 還提供了單元測試套件以自動執(zhí)行單元測試。
結(jié)束語 在 本文中,我向您介紹了如何使用 Dojo 工具包、JSON 和 JSON-RPC 為 Java EE 應(yīng)用程序構(gòu)建 Ajax 客戶端。我概述了每項技術(shù),并介紹了一些設(shè)計原則。最后,我提供了一個示例應(yīng)用程序。Ajax 應(yīng)用程序?qū)⒑芸斐蔀?SOA 客戶端。通過使用 Dojo 之類的工具包,您可以方便地構(gòu)建這些網(wǎng)頁。
致謝 非常感謝 Chris Mitchell 審閱了這篇文章并提出了寶貴的意見。
下載 描述 | 名字 | 大小 | 下載方法 | Sample code for this article | DojoJ2EE.war | 1.8MB | FTP|HTTP |
參考資料 學(xué)習(xí) 獲得產(chǎn)品和技術(shù) |