DOJO + JSON
曾幾何時(shí)ajax已經(jīng)遍布大江南北,然而單純的js難于閱讀和debug,dojo的出現(xiàn),無(wú)疑部分地解決了這個(gè)問(wèn)題,加上ide的支持,更是如虎添翼了!引用一篇IBM的文章,看看dojo的應(yīng)用吧。
以下是引用內(nèi)容:
使用 Dojo 工具包和 JSON-RPC 構(gòu)建企業(yè) SOA Ajax 客戶端![]() |
![]() |
![]() |
|
級(jí)別: 中級(jí)
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) 來(lái)調(diào)用服務(wù)器端 Java 對(duì)象。
異步 JavaScript 和 XML (Ajax) 是使用本機(jī)瀏覽器技術(shù)構(gòu)建富 Web 應(yīng)用程序的新方法。對(duì)于編寫(xiě)需要某些類型的“活動(dòng)”用戶界面的復(fù)雜應(yīng)用程序的開(kāi)發(fā)人員,JavaScript 在這方面已經(jīng)做得很好。不過(guò),JavaScript 難于編碼、調(diào)試、移植和維護(hù)。使用 Ajax 工具包有助于最大程度地減少使用 JavaScript 和 Ajax 帶來(lái)的許多常見(jiàn)問(wèn)題。優(yōu)秀的 Ajax 工具包提供了一組可重用的小部件、用于擴(kuò)展和創(chuàng)建小部件的框架、事件系統(tǒng)、JavaScript 實(shí)用工具和增強(qiáng)的異步服務(wù)器調(diào)用支持。在本文中,我們將討論如何使用 Dojo 工具包為 Java EE 應(yīng)用程序構(gòu)建企業(yè) SOA 客戶端。我們還將使用 JSON (JavaScript Object Notation)–RPC 來(lái)調(diào)用服務(wù)器端 Java 對(duì)象。
在本文中,我們還將向您提供以下內(nèi)容的簡(jiǎn)要說(shuō)明:Ajax、Dojo、JSON 和 JSON-RPC,以及一些設(shè)計(jì) Ajax 應(yīng)用程序的設(shè)計(jì)原則和您可以下載并親自嘗試運(yùn)行的簡(jiǎn)短示例。
![]() ![]() |
![]()
|
有許多關(guān)于 Ajax 的論文、文章和書(shū)籍。我不打算對(duì) Ajax 進(jìn)行深入介紹。有關(guān)詳細(xì)信息,請(qǐng)查閱參考資料。
Ajax 可作為使用本機(jī)瀏覽器組件構(gòu)建網(wǎng)站的體系結(jié)構(gòu)樣式。Ajax 的關(guān)鍵部分有:
- JavaScript,它可以編排頁(yè)面元素,從而獲得最佳 Ajax 用戶體驗(yàn)。
- Cascading Style Sheets (CSS),它可以定義頁(yè)面元素的可視樣式。
- 文檔對(duì)象模型(Document Object Model,DOM),它將網(wǎng)頁(yè)結(jié)構(gòu)作為一組可以使用 JavaScript 操作的可編程對(duì)象提供。
- XMLHttpRequest,它支持以后臺(tái)活動(dòng)的形式從 Web 資源檢索數(shù)據(jù)。
XMLHttpRequest 對(duì)象是關(guān)鍵部分。
XMLHttpRequest 對(duì)象是 Ajax 用于進(jìn)行異步請(qǐng)求的機(jī)制。圖 1 說(shuō)明了該流程:
圖 1. XMLHttpRequest 對(duì)象進(jìn)行異步請(qǐng)求的流程圖

XMLHttpRequest 對(duì)象是瀏覽器中提供的 JavaScript 對(duì)象。(Microsoft? 和 Mozilla 瀏覽器各有自已的版本)。該流程如下所示:
- 頁(yè)面調(diào)用某個(gè) JavaScript。
- JavaScript 函數(shù)創(chuàng)建 XMLHttpRequest 對(duì)象。這包括設(shè)置要調(diào)用的 URL 和 HTTP 請(qǐng)求參數(shù)。
- JavaScript 函數(shù)注冊(cè)回調(diào)處理程序。HTTP 響應(yīng)調(diào)用此回調(diào)處理程序。
- JavaScript 函數(shù)調(diào)用
XMLHttpRequest
對(duì)象上的send
方法,該方法接著將 HTTP 請(qǐng)求發(fā)送到服務(wù)器。 -
XMLHttpRequest
對(duì)象立即將控制返回到 JavaScript 方法。此時(shí),用戶可以繼續(xù)使用該頁(yè)面。 - 稍后,HTTP 服務(wù)器通過(guò)調(diào)用回調(diào)處理程序返回 HTTP 響應(yīng)。
- 回調(diào)處理程序可以訪問(wèn) HTML DOM 對(duì)象。它可以動(dòng)態(tài)更新頁(yè)面元素,而無(wú)需中斷用戶(除非您碰巧更新用戶正在使用的 DOM 對(duì)象)。
通過(guò)異步更新頁(yè)面的 DOM,還可以在本地進(jìn)行異步請(qǐng)求。
![]() ![]() |
![]()
|
Dojo 使您能夠方便地構(gòu)建動(dòng)態(tài)站點(diǎn)。它提供一個(gè)豐富的小部件庫(kù),您可以使用它組成頁(yè)面。您可以使用基于 Dojo 方面的事件系統(tǒng)將事件附加到組件,以創(chuàng)建豐富的交互體驗(yàn)。此外,您可以使用幾個(gè) Dojo 庫(kù)進(jìn)行異步服務(wù)器請(qǐng)求、添加動(dòng)畫(huà)效果和瀏覽存儲(chǔ)實(shí)用工具等等。
Dojo 提供了您可以用于構(gòu)建頁(yè)面的一組豐富的小部件。您可以使用多個(gè)方法創(chuàng)建 Dojo 小部件。Dojo 的眾多優(yōu)點(diǎn)之一是它允許您使用標(biāo)準(zhǔn)的 HTML 標(biāo)記。然后,可以將這些標(biāo)記用于小部件。這樣,HTML 開(kāi)發(fā)人員就可以方便地使用 Dojo,如清單 1 所示:
清單 1. 在 HTML 標(biāo)記中使用 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
標(biāo)記來(lái)定義小部件的位置,而在頁(yè)面加載或?qū)κ录M(jìn)行響應(yīng)時(shí) Dojo 可以在這些地方放置小部件。您還可以使用更具體的標(biāo)記,如 <dojo:widget>
,并向其中添加 Dojo 小部件屬性。在清單 1 中,我們將 dojoType
屬性添加到 button
標(biāo)記。在設(shè)置了標(biāo)記之后,您需要在一些 JavaScript 內(nèi)部加載小部件,如清單 2 所示。您可以將標(biāo)記嵌入到頁(yè)面內(nèi)部,但是我們建議將其放置在單獨(dú)的 JS 文件中。在本文的稍后部分中,我們將闡明一些 MVC 設(shè)計(jì)原則。
清單 2. 在 HTML 標(biāo)記中使用 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)建、訪問(wèn)、修改和刪除小部件,從而實(shí)現(xiàn)動(dòng)態(tài)行為。在我們的示例中,您將看到在 JavaScript 中訪問(wèn)小部件的示例。
Dojo 事件系統(tǒng)使用面向方面的技術(shù)將事件附加到小部件。這可以將小部件與實(shí)際的事件處理分離。Dojo 不是將硬代碼 JavaScript 事件添加到 html
標(biāo)記上,而是提供允許您將事件附加到小部件的 API,如清單 3 所示。
清單 3. 使用 Dojo 將事件處理程序附加到小部件
function submitStock()
{
...
}
function init()
{
var stockButton = dojo.widget.byId('stockButton');
dojo.event.connect(stockButton, 'onClick', 'submitStock');
}
dojo.addOnLoad(init);
|
通過(guò)使用 connect
方法,您可將 JavaScript 方法連接到小部件。您還可以在 div
節(jié)點(diǎn)上附加 dojoAttachEvent
,如下所示。某些 HTML 標(biāo)記沒(méi)有定義事件,所以這是一個(gè)方便的擴(kuò)展。
清單 4. 使用 Dojo 將事件附加到 HTML 標(biāo)記
<div dojoAttachPoint="divNode"
dojoAttachEvent="onClick; onMouseOver: onFoo;">
|
Dojo 事件系統(tǒng)還允許多個(gè)高級(jí)功能,如:
- 聲明在現(xiàn)有的事件處理程序之前或之后插入事件處理程序的建議。
- 允許小部件在瀏覽器中訂閱或發(fā)布主題。
- 添加事件回調(diào)。
- 可用于表示事件的
event
規(guī)范化對(duì)象。
有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn) http://dojo.jot.com/EventExamples。
Dojo 通過(guò)抽象特定于瀏覽器的詳細(xì)信息,提供了對(duì)服務(wù)器進(jìn)行異步請(qǐng)求的簡(jiǎn)單方法。Dojo 允許您創(chuàng)建數(shù)據(jù)結(jié)構(gòu)來(lái)隱藏詳細(xì)信息,如清單 5 所示。
清單 5. 使用 Dojo 進(jìn)行異步請(qǐng)求
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 標(biāo)準(zhǔn)支持 RPC。在接下來(lái)的部分中,我們將看一看 Dojo 對(duì) JSON 的支持。
Dojo 是一個(gè)具有許多功能的豐富庫(kù),包括:
- 處理 html、字符串、樣式、dom、正則表達(dá)式和若干其他實(shí)用工具的通用庫(kù)。
- 包括字典、ArraryLists、隊(duì)列、SortedList、設(shè)置和堆棧的數(shù)據(jù)結(jié)構(gòu)。
- 用于添加動(dòng)畫(huà)效果、驗(yàn)證、拖放和若干其他功能的可視化 Web 實(shí)用工具。
- 數(shù)學(xué)和加密庫(kù)。
- 存儲(chǔ)組件。
- XML 解析
有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn) http://manual.dojotoolkit.org/index.html。
![]() ![]() |
![]()
|
JSON 是 JavaScript 的對(duì)象文字符號(hào)的子集,它是在 JavaScript 中表示數(shù)據(jù)結(jié)構(gòu)的常用方法。JSON 是一種完全與語(yǔ)言無(wú)關(guān)的文本格式,但使用編程人員熟悉的與 C 語(yǔ)言家族(包括 C、C++、C#、Java、JavaScript、Perl、Python 和許多其他語(yǔ)言)類似的約定。這些屬性使 JSON 成為 Ajax 客戶端的理想數(shù)據(jù)交換語(yǔ)言。
JSON 構(gòu)建在兩種結(jié)構(gòu)的基礎(chǔ)上:
- 名稱/值對(duì)的集合。在不同的語(yǔ)言中,它被實(shí)現(xiàn)為對(duì)象、記錄、結(jié)構(gòu)、字典、哈希表、有鍵列表或者關(guān)聯(lián)數(shù)組。
- 值的有序列表。在大多數(shù)語(yǔ)言中,它被實(shí)現(xiàn)為數(shù)組、向量、列表或序列。
JSON 對(duì)象的示例如下:
var myJSONObject = {"id": 4, "name": "Roland Barcia", "pets": ["dog","cat","fish"]}; |
在示例中,我們對(duì)值對(duì)進(jìn)行了命名。括號(hào)中的任何內(nèi)容都是一個(gè)列表。在不同的編程語(yǔ)言中都有一組豐富的實(shí)現(xiàn)。有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn) http://json.org/。
JSON-RPC 是一種輕量級(jí)遠(yuǎn)程過(guò)程調(diào)用協(xié)議,在此協(xié)議中,JSON 可以連續(xù)請(qǐng)求和響應(yīng)。向遠(yuǎn)程服務(wù)發(fā)送請(qǐng)求可以調(diào)用遠(yuǎn)程方法。該請(qǐng)求是具有三個(gè)屬性的單個(gè)對(duì)象:
-
method
- 包含要調(diào)用的方法名稱的字符串。 -
params
- 作為參數(shù)傳遞到方法的對(duì)象數(shù)組。 -
id
- 請(qǐng)求 ID。它可以屬于任何類型。它用于將響應(yīng)與其應(yīng)答的請(qǐng)求相匹配。
當(dāng)方法調(diào)用完成時(shí),服務(wù)必須對(duì)響應(yīng)進(jìn)行應(yīng)答。此響應(yīng)是具有三個(gè)屬性的單個(gè)對(duì)象:
-
result
- 被調(diào)用方法返回的對(duì)象。它必須為null
,以避免在調(diào)用該方法時(shí)發(fā)生錯(cuò)誤。 -
error
-error
對(duì)象(如果在調(diào)用方法時(shí)發(fā)生錯(cuò)誤)。它必須為null
(如果不存在任何錯(cuò)誤)。 -
id
- 它必須是與響應(yīng)的請(qǐng)求相同的 ID。
通知是沒(méi)有響應(yīng)的特殊請(qǐng)求。它與帶有一個(gè)異常的請(qǐng)求對(duì)象具有相同的屬性:
-
id
- 必須為null
。
XML 是一種用于面向服務(wù)的體系結(jié)構(gòu) (SOA) 和數(shù)據(jù)傳輸?shù)某R?jiàn)傳輸。當(dāng)然,目前許多服務(wù)以 SOAP 格式存在。不過(guò),何時(shí)將其用于數(shù)據(jù)傳輸在 Ajax 社區(qū)中存在分岐。JSON 有以下幾個(gè)優(yōu)點(diǎn):
- 瀏覽器解析 JSON 的速度比 XML 快。
- JSON 構(gòu)造是友好的編程語(yǔ)言,并容易轉(zhuǎn)換為后端編程語(yǔ)言(如 Java)。
- JSON 相當(dāng)穩(wěn)定。JSON 的附加內(nèi)容將成為超集。
XML 有以下優(yōu)點(diǎn):
- 調(diào)用將 XML 用作傳輸?shù)默F(xiàn)有服務(wù)。
- 使用 XSLT 可以動(dòng)態(tài)轉(zhuǎn)換 XML。這是企業(yè)服務(wù)總線 (ESB) 方案中的理想功能。
Dojo 為調(diào)用 JSON-RPC 請(qǐng)求提供了抽象層。用于 Dojo 的 JSON-RPC 引入了標(biāo)準(zhǔn)方法描述(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);
這將通過(guò)網(wǎng)絡(luò)發(fā)送此結(jié)構(gòu):
{"id": 2, "method": "getStockData", "params": ["IBM"]}
JSON-RPC 為遠(yuǎn)程過(guò)程調(diào)用定義標(biāo)準(zhǔn)格式,但是不存在對(duì)后端技術(shù)的標(biāo)準(zhǔn)映射。JSON-RPC Java Orb 提供了這樣一種機(jī)制:注冊(cè) Java 對(duì)象,并將它們公開(kāi)為 JSON-PRC 服務(wù)。它還在 JavaScript 中提供客戶端 API,以調(diào)用服務(wù)。
如果您選擇使用 Dojo,則可以編寫(xiě)自已的綁定代碼。用于 Java 的 JSON API 可以提供幫助。有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn) (http://developer.berlios.de/projects/jsontools/)。在我們的示例中,我們將使用 JSON-RPC Java ORB 進(jìn)行異步請(qǐng)求,以利用服務(wù)器端綁定代碼。
JSON-RPC Java Orb 允許您在一種 Servlet 范圍(請(qǐng)求、會(huì)話、應(yīng)用程序)內(nèi)注冊(cè) Java 對(duì)象。然后,它可以使用 JSON-RPC 請(qǐng)求來(lái)調(diào)用 Java 對(duì)象。為此,可以將對(duì)象類型放在 JSON 對(duì)象之前。由于 Dojo API 不執(zhí)行此操作,所以用于 JSON-RPC 的 Dojo 客戶端 API 與用于 Java 的 JSON-RPC 不兼容。
清單 7 提供了如何向 HttpSession 注冊(cè) Java 對(duì)象的示例:
清單 7. 注冊(cè) Java 對(duì)象的 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 客戶端寫(xiě)入到 Java 服務(wù),如清單 8 所示。
清單 8. 連接 Java 服務(wù)的 JSONRpcClient
jsonrpc = new JSONRpcClient("/DojoJ2EE/JSON-RPC"); var stockButton = dojo.byId('stockInput'); jsonrpc.StockService.getStockData(stockResultCallBack,stockButton.value); |
此請(qǐng)求會(huì)發(fā)送以下有效負(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),并向您提供一個(gè)靜態(tài)接口。
為了支持請(qǐng)求,您需要注冊(cè)特殊的 Servlet。稍后,我將向您介紹如何執(zhí)行此操作。
JSON-RPC Java ORB 的一個(gè)缺點(diǎn)是只有單個(gè) URL,這導(dǎo)致使用 Java EE 安全來(lái)保護(hù) URL 非常困難。作為一種變通方法,您可以在 HTTP 會(huì)話層注冊(cè)服務(wù),并根據(jù)安全需要添加它們。
![]() ![]() |
![]()
|
為企業(yè)應(yīng)用程序構(gòu)建企業(yè)客戶端
在此部分中,我將討論一些設(shè)計(jì)原則,然后詳細(xì)講解一個(gè)示例。您可以下載完整的 WAR 文件,并親自嘗試該應(yīng)用程序。
在 Java EE 領(lǐng)域中,模型-視圖-控制器 (MVC) 已經(jīng)變得非常成熟,這歸功于 Struts 和 JavaServer Faces 之類的框架。使用 Ajax 可以進(jìn)行正確的 MVC 設(shè)計(jì),這對(duì)成功的 Web 應(yīng)用程序至關(guān)重要。此外,SOA 允許直接從 Ajax 應(yīng)用程序調(diào)用服務(wù)。這樣做有幾個(gè)優(yōu)點(diǎn),如 WebSphere 期刊文章 "AJAX Requests – Data or Markup?" 中所述。
圖 2 提供了 Ajax 客戶端和 Java EE 應(yīng)用程序之間理想生態(tài)系統(tǒng)的簡(jiǎn)要概述。
圖 2. Ajax 客戶端和 Java EE 應(yīng)用程序之間的理想生態(tài)系統(tǒng)

在服務(wù)器端,業(yè)務(wù)和門戶功能現(xiàn)在被公開(kāi)為某一類型的服務(wù)接口。在服務(wù)的形式下,應(yīng)遵循正確的服務(wù)器端最佳實(shí)踐。服務(wù)器應(yīng)用程序應(yīng)公開(kāi)過(guò)程粒度服務(wù)。JavaServer Faces 之類的框架現(xiàn)在負(fù)責(zé)執(zhí)行初始呈現(xiàn);不過(guò),對(duì)于客戶端 Ajax 工具包,這是可選的。
在瀏覽器上,分離關(guān)注的內(nèi)容非常重要。圖 3 突出顯示了 Java 服務(wù)器文件結(jié)構(gòu):
圖 3. Java 服務(wù)器文件結(jié)構(gòu)

您可以選擇每頁(yè)有一個(gè) JavaScript 控制器。不過(guò),對(duì)于復(fù)雜的門戶頁(yè),您可以將相關(guān)事件分組成小型的控制器集。
控制器文件應(yīng):
- 向小部件加載網(wǎng)絡(luò)請(qǐng)求處理程序,如圖 4 所示:
圖 4. 將請(qǐng)求處理程序附加到小部件 - 實(shí)現(xiàn)請(qǐng)求處理程序。請(qǐng)求處理程序不應(yīng)有太多的邏輯。它們應(yīng)委派給服務(wù) Facade,以便與后端交互。
- 實(shí)現(xiàn)回調(diào)處理程序方法。回調(diào)應(yīng)將呈現(xiàn)委派給獨(dú)立 JS 文件。此外,它們應(yīng)將存儲(chǔ)中間狀態(tài)的工作委派給獨(dú)立 Java 服務(wù)器文件。對(duì)于無(wú)狀態(tài)交互,可以直接將結(jié)果傳遞到 rendering.js 文件。
圖 5 說(shuō)明了組件之間的流:
圖 5. 從客戶端到請(qǐng)求處理程序,再到回調(diào)處理程序的信息流

呈現(xiàn)文件包含呈現(xiàn)組件的邏輯或基于事件結(jié)果的用戶界面。
Business Facades 應(yīng)包含代理服務(wù)器請(qǐng)求的方法。DataCopy 應(yīng)維護(hù)需要本地保存在瀏覽器中的本地視圖對(duì)象。
為 Dojo 設(shè)置 Java EE 應(yīng)用程序
對(duì)于 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。還需要將單個(gè) jsonrpc.js 文件添加到 Web 內(nèi)容文件夾中,如圖 7 所示:
圖 7. 添加使用 JSON-RPC Java Orb 所必需的 JavaScript 文件

為了使 Java EE 應(yīng)用程序能夠接收用于 Java 請(qǐng)求的 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> |
Ajax 使 SOA 客戶端更完美。在我們的示例中,我們使用了一個(gè)簡(jiǎn)單的 Java 服務(wù)。
圖 8 是基于 Java 的服務(wù)模型:
圖 8. 基于 Java 的服務(wù)模型

我們使用了一個(gè)簡(jiǎn)單的硬編碼實(shí)現(xiàn),如清單 10 所示:
清單 10. 簡(jiǎn)單的硬編碼 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 公開(kāi) Java 服務(wù)
為了公開(kāi) Java 應(yīng)用程序,我使用了被稱為 ExportServices 的 HttpSessionListener,以便為用戶注冊(cè)服務(wù),如清單 11 所示:
清單 11. ExportServices,即公開(kāi) 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) {
}
}
|
您需要將偵聽(tīng)器添加到應(yīng)用程序中(通過(guò)將其添加到 web.xml),如清單 12 所示:
清單 12. 添加到 web.xml 的 ExportServices 偵聽(tīng)器
<listener> <listener-class>ExportServices</listener-class> </listener> |
設(shè)置了基礎(chǔ)結(jié)構(gòu)并公開(kāi)了服務(wù)之后,現(xiàn)在我們可以構(gòu)建 Web 客戶端了。通過(guò) Dojo,我們利用小部件構(gòu)建網(wǎng)頁(yè)并利用事件模型。圖 9 說(shuō)明了建議的開(kāi)發(fā)過(guò)程:
圖 9. 開(kāi)發(fā)過(guò)程示例

我將使用此過(guò)程演示該示例。
從小部件構(gòu)建 UI
首先構(gòu)建 UI。請(qǐng)參見(jiàn)清單 13,了解示例 UI。
創(chuàng)建 UI:
- 加載腳本:
- dojo
- jsonrpc
- StockClientController
- resultRenderer
- 構(gòu)建頁(yè)面,并結(jié)合使用
div
和 HTML 標(biāo)記以創(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)鍵。在腳本的開(kāi)頭,使用
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();
操作前后需要考慮的事項(xiàng)
在 Ajax 中,需要考慮的一件事是,在觸發(fā)事件之前,不要顯示某些用戶界面。不過(guò),一種做法是放置 div
標(biāo)記作為占位符。然后,可以使用 DOM 或 Dojo API 訪問(wèn)此區(qū)域,并添加動(dòng)態(tài) UI 元素。在我們的應(yīng)用程序中,我添加了一個(gè)簡(jiǎn)單的 div
,以獲得以下結(jié)果:
<div id="resultArea" />
附加樣式表
接下來(lái),使用 CSS 添加樣式。CSS 是設(shè)置 Ajax 應(yīng)用程序格式的標(biāo)準(zhǔn)方法。使用 CSS,您可以將樣式定義應(yīng)用于多個(gè) div
標(biāo)記,方法是將標(biāo)記的 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ù)視圖
接下來(lái),一個(gè)好的想法是確保 UI 開(kāi)發(fā)人員在 JavaScript 中擁有一個(gè)服務(wù)視圖。Dojo 使用 SMD 來(lái)做到這一點(diǎn),如前面所述。用于 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)建請(qǐng)求事件處理程序
接著,在控制器 JS 文件中,我們需要?jiǎng)?chuàng)建事件處理程序和回調(diào)處理程序。回調(diào)處理程序應(yīng)是其他工作的 Facade。在我們的示例中,事件處理程序?qū)惒秸{(diào)用我們的服務(wù),并將回調(diào)傳遞到相應(yīng)的方法。XMLHttpRequest
對(duì)象的此抽象由 JSON-RPC-Java 提供。在接收到響應(yīng)時(shí),回調(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); } } |
在加載時(shí)加載初始 UI 和網(wǎng)絡(luò)請(qǐng)求事件
下面,我們?cè)陧?yè)面初始化時(shí)使用 Dojo 這種有力的工具,將小部件連接到請(qǐng)求處理程序。請(qǐng)參見(jiàn)清單 18 中的 init
方法。dojo.addOnLoad()
方法允許您使用同一面向方面的技術(shù),將 init
方法附加到頁(yè)面加載事件。
清單 18. init() 方法
function init()
{
var stockButton = dojo.widget.byId('stockButton');
dojo.event.connect(stockButton, 'onClick', 'submitStock');
}
dojo.addOnLoad(init);
|
呈現(xiàn)響應(yīng)
最后一步是添加動(dòng)態(tài)呈現(xiàn)響應(yīng)代碼。我將它放置在獨(dú)立呈現(xiàn)器 JS 文件中。您可以使用各種方法來(lái)呈現(xiàn)響應(yīng)。在我們的示例中,我們將結(jié)合使用 DOM API 和 Dojo 實(shí)用工具來(lái)構(gòu)建簡(jiǎn)單的表。在這里,我們可以使用 Dojo 的小部件之一,但是我希望對(duì)清單 19 中的函數(shù) renderStockResult
使用自已的代碼,以便突出顯示一些 Dojo 實(shí)用工具和數(shù)據(jù)結(jié)構(gòu)。
要?jiǎng)?chuàng)建呈現(xiàn)響應(yīng)代碼,請(qǐng)執(zhí)行下列操作:
- 在
renderStockResult
函數(shù)中,使用dojo.byId()
方法訪問(wèn)resultArea
對(duì)象。 - 檢查任何異常;如果
renderStockResult
含有傳遞給它的異常,它會(huì)將該異常傳遞到錯(cuò)誤處理程序并返回。 - 使用
Dictionary
(類似于 JavaHashMap
)和ArrayList
Dojo 結(jié)構(gòu)來(lái)存放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
方法。實(shí)際上,此類實(shí)用工具將會(huì)被外部化。在下面顯示的方法中,我們將使用 Dojo Iterator
對(duì)象來(lái)遍歷這些數(shù)據(jù)結(jié)構(gòu)并創(chuàng)建表。我要在下面指出的另一個(gè)方法是 dojo.fx.html.fadeShow(table, 200)
,您可以使用該方法將淡入效果添加到結(jié)果的打印中。這只是某些動(dòng)畫(huà)的一瞬。在清單 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); } } } |
最后,我們將添加簡(jiǎn)單的錯(cuò)誤處理方法,以打印錯(cuò)誤消息,如清單 21 所示。請(qǐng)記住,通過(guò)在元素上設(shè)置類屬性,然后委派給 CSS 文件,可添加粗體文本。
清單 21. 錯(cuò)誤處理方法
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;
}
|
測(cè)試應(yīng)用程序
您可以下載應(yīng)用程序的最終 WAR 文件。可將其安裝在任何應(yīng)用服務(wù)器(如 WebSphere Application Server)上。部署了應(yīng)用程序后,您可以打開(kāi) HTML 頁(yè),以查看浮點(diǎn)形式,如圖 10 所示:
圖 10. HTML 中的浮點(diǎn)形式

可以自由移動(dòng)該形式。您可能注意到,我已將浮點(diǎn)綁定到外部容器,如圖 11 所示:
圖 11. 綁定到外部容器的浮點(diǎn)

輸入 IBM
以調(diào)用 Java EE 應(yīng)用程序。結(jié)果應(yīng)類似于圖 12 所示的形式:
圖 12. 將 IBM 輸入到應(yīng)用程序的結(jié)果

接下來(lái),輸入一些其他數(shù)據(jù),以測(cè)試錯(cuò)誤處理程序,如圖 13 所示。
圖 13. 測(cè)試錯(cuò)誤處理程序

Dojo 還提供了單元測(cè)試套件以自動(dòng)執(zhí)行單元測(cè)試。
![]() ![]() |
![]()
|
在本文中,我向您介紹了如何使用 Dojo 工具包、JSON 和 JSON-RPC 為 Java EE 應(yīng)用程序構(gòu)建 Ajax 客戶端。我概述了每項(xiàng)技術(shù),并介紹了一些設(shè)計(jì)原則。最后,我提供了一個(gè)示例應(yīng)用程序。Ajax 應(yīng)用程序?qū)⒑芸斐蔀?SOA 客戶端。通過(guò)使用 Dojo 之類的工具包,您可以方便地構(gòu)建這些網(wǎng)頁(yè)。
引用:http://www-128.ibm.com/developerworks/cn/websphere/library/techarticles/0606_barcia/0606_barcia.html
posted on 2007-03-06 16:54 whirlyzhq 閱讀(2172) 評(píng)論(0) 編輯 收藏 所屬分類: framework