亚洲AV成人无码久久精品老人,精品亚洲国产成人av,亚洲AV一区二区三区四区http://www.tkk7.com/faithwind/category/7322.htmlLove Java ,because you are my first lady !^_^zh-cnTue, 27 Feb 2007 21:46:34 GMTTue, 27 Feb 2007 21:46:34 GMT60創(chuàng)建國(guó)際化的 JSP 應(yīng)用程序http://www.tkk7.com/faithwind/articles/30445.html黑咖啡黑咖啡Mon, 13 Feb 2006 06:10:00 GMThttp://www.tkk7.com/faithwind/articles/30445.htmlhttp://www.tkk7.com/faithwind/comments/30445.htmlhttp://www.tkk7.com/faithwind/articles/30445.html#Feedback0http://www.tkk7.com/faithwind/comments/commentRss/30445.htmlhttp://www.tkk7.com/faithwind/services/trackbacks/30445.html

了解服務(wù)器端應(yīng)用程序所面臨的獨(dú)特挑戰(zhàn)

 
將此頁(yè)作為電子郵件發(fā)送

將此頁(yè)作為電子郵件發(fā)送

未顯示需要 JavaScript 的文檔選項(xiàng)

樣例代碼


對(duì)此頁(yè)的評(píng)價(jià)

幫助我們改進(jìn)這些內(nèi)容


級(jí)別: 初級(jí)

2005 年 3 月 29 日

為國(guó)際客戶設(shè)計(jì) Java Server Pages(JSP)應(yīng)用程序更像是一門藝術(shù),而不像是科學(xué),但所涉及的內(nèi)容不僅僅能滿足眼球。成功的關(guān)鍵是理解與國(guó)際化有關(guān)的獨(dú)一無二的服務(wù)器端問題。Java 開發(fā)人員 Sing Li 將闡述這個(gè)重要問題,并給出兩個(gè)經(jīng)過考驗(yàn)確實(shí)有效的解決方案。

世界經(jīng)濟(jì)日益全球化推動(dòng)了人們對(duì)基于 Web 的軟件的需求,因?yàn)樵S多國(guó)家的用戶都能訪問 Web 軟件。這些用戶使用的語(yǔ)言、顯示、數(shù)據(jù)錄入、表示和文化需求等都可能存在很大的差異。國(guó)際化(縮寫為 i18n)是一種創(chuàng)建為分散用戶群工作的應(yīng)用程序的藝術(shù)。

也許有點(diǎn)讓人感到驚訝,當(dāng)不作任何定制修改就在服務(wù)器端使用時(shí),J2SE 對(duì)國(guó)際化的內(nèi)建支持會(huì)表現(xiàn)出一些不足。一般來說,服務(wù)器端的國(guó)際化仍然是一門藝術(shù),而不是一項(xiàng)科學(xué),它常常涉及一些專用的或用戶自主開發(fā)的解決方案。

本文把服務(wù)器端基于 JSP 的應(yīng)用程序的國(guó)際化需求與 J2SE 應(yīng)用程序的國(guó)際化需求區(qū)別開來。本文將介紹導(dǎo)致服務(wù)器端需求明顯不同的各種客戶機(jī)/服務(wù)器技術(shù)。然后,還將查看實(shí)際代碼,這些代碼展示了用來解決基本問題的、廣泛采用的兩個(gè)解決方案。

超越 J2SE 地區(qū)

J2SE 用地區(qū)(locale) 的概念(請(qǐng)參閱Notation for a locale)進(jìn)行國(guó)際化。在一臺(tái)機(jī)器上,地區(qū)代表用戶選擇的顯示語(yǔ)言(例如,英語(yǔ)或西班牙語(yǔ)),以及日期、時(shí)間、貨幣等方面的格式化約定。通常,由底層操作系統(tǒng)管理地區(qū)選項(xiàng)設(shè)置,并在運(yùn)行的時(shí)候把它傳遞給 J2SE。

如果運(yùn)行的服務(wù)器在本地機(jī)器上,或者是在局域網(wǎng)上,那么特定于機(jī)器的地區(qū)的概念就很好用。包含的所有客戶機(jī)和服務(wù)器都在同一地區(qū),所以它們都使用相同的顯示語(yǔ)言、日期約定等。這些場(chǎng)景都不會(huì)帶來麻煩的國(guó)際化問題。但是如果想用同一臺(tái)服務(wù)器為多個(gè)國(guó)際位置上的用戶提供服務(wù)的話,那么情況就變得很復(fù)雜。


服務(wù)器端國(guó)際化問題

當(dāng)為了進(jìn)行國(guó)際化訪問而部署服務(wù)器應(yīng)用程序時(shí),該應(yīng)用程序必須同時(shí)為不同的地區(qū)提供支持。圖 1 顯示了一種可能的場(chǎng)景。每臺(tái)機(jī)器 —— 服務(wù)器以及可在任何時(shí)間訪問服務(wù)器的客戶機(jī) —— 都可能有自己的地區(qū)設(shè)置,而這些地區(qū)是不同的。

地區(qū)的概念

地區(qū)是以語(yǔ)言代碼后跟著的國(guó)家代碼的形式指定的。(請(qǐng)參閱參考資料,以獲得這些代碼的更多信息。)標(biāo)準(zhǔn)格式是 xx_YY,其中 xx 兩個(gè)小寫字母表示的語(yǔ)言代碼,YY 是兩個(gè)大寫字母表示的國(guó)家代碼。也有可能看見在語(yǔ)言和國(guó)家代碼之間使用連字符,或者把國(guó)家代碼寫成小寫形式的變體。把語(yǔ)言從國(guó)家分出來是必需的,因?yàn)榭赡懿恢挂粋€(gè)國(guó)家要使用相同的語(yǔ)言,但是日期、時(shí)間、貨幣格式等可能有不同的表達(dá)習(xí)慣。


圖 1. 為不同地區(qū)的用戶提供服務(wù)的服務(wù)器
為不同地區(qū)的用戶提供服務(wù)的服務(wù)器

在圖 1 中,服務(wù)器機(jī)器位于 San Francisco,特定于機(jī)器的地區(qū)是 en_US(美國(guó)英語(yǔ))。來自紐約和達(dá)拉斯的用戶都使用 en_US 地區(qū),所以沒有額外的國(guó)際化需求。但是,來自漢城的用戶期望看到用韓語(yǔ)表示的應(yīng)用程序提示,他們的地區(qū)是 ko_KR。同時(shí),來自上海的用戶想看到中文表示的應(yīng)用程序文本,他們的地區(qū)是 zh_CN。來自東京的用戶期望用日語(yǔ)顯示應(yīng)用程序,他們的地區(qū)是 ja_JP。所有這些用戶的需求都必須通過運(yùn)行在 San Francisco 服務(wù)器上的 JSP 應(yīng)用程序來得到滿足。

在服務(wù)器端,您對(duì)于服務(wù)器機(jī)器自己的地區(qū)擁有全部控制權(quán),但您無法改變客戶機(jī)的地區(qū)或強(qiáng)行將它轉(zhuǎn)換成某個(gè)特定地區(qū)。相反,應(yīng)用程序必須識(shí)別出用戶的地區(qū),并保證 JSP 頁(yè)面以正確的本地化形式出現(xiàn)(請(qǐng)參閱 Detecting client locale)。

檢測(cè)客戶機(jī)地區(qū)

通過檢查從用戶瀏覽器傳入的 HTTP 頭,可以自動(dòng)檢測(cè)用戶的地區(qū)。但是,用戶或操作系統(tǒng)可能沒有正確地設(shè)置這條信息。再加上這樣的事實(shí),即一些瀏覽器處理地區(qū)的信息存在 bug,有了這些,您就不難理解,為什么讓應(yīng)用程序顯式詢問用戶首選地區(qū)是一個(gè)更能接受的確定地區(qū)值的方法。

更多地區(qū)判斷的復(fù)雜情況

就在您以為可以安全地判斷客戶機(jī)地區(qū),并據(jù)此呈現(xiàn) JSP 時(shí),新的問題出現(xiàn)了。請(qǐng)考慮以下這些非常現(xiàn)實(shí)的場(chǎng)景。

來自東京的一位訪問人員工正在使用位于中國(guó)上海的銷售辦公室的機(jī)器,機(jī)器的地區(qū)是 zh_CN。因?yàn)椴皇煜嬷形模运胗萌照Z(yǔ)訪問 Web 應(yīng)用程序。關(guān)于這種情況的說明,參見圖 2(a)。


圖 2. 用戶期望的地區(qū)與客戶端機(jī)器上的地區(qū)不同
用戶期望的地區(qū)與客戶端機(jī)器上的地區(qū)不同

在圖 2(a),San Francisco 服務(wù)器的地區(qū)是 en_US。客戶端機(jī)器在上海,地區(qū)是 zh_CN。但是 JSP 應(yīng)用程序需要用 ja_JP 地區(qū)顯示,對(duì)用戶才有用。

再看另外一個(gè)更加怪異但并非不可能的場(chǎng)景。國(guó)際化 JSP 應(yīng)用程序的開發(fā)人員正在一項(xiàng)調(diào)試。在一個(gè)地區(qū)為 en_US 的客戶端系統(tǒng)上,打開了三個(gè)運(yùn)行 JSP 應(yīng)用程序的瀏覽器實(shí)例。服務(wù)器機(jī)器在局域網(wǎng)上,地區(qū)也是 en_US。但是,現(xiàn)在要為中文和日語(yǔ)用戶測(cè)試應(yīng)用程序的處理能力。所以在一臺(tái) en_US 客戶端機(jī)器上,一個(gè)瀏覽器實(shí)例用的是英文(en_US),一個(gè)用的是日語(yǔ)(ja_JP),還有一個(gè)用的是中文(zh_CN)。圖 2(b)演示了這種情況。

目前為止,基本的國(guó)際化問題應(yīng)當(dāng)很清楚了:在處理顯示國(guó)際化應(yīng)用程序的任意特殊實(shí)例所需的實(shí)際地區(qū)時(shí),客戶機(jī)地區(qū)和服務(wù)器地區(qū)都有些力不從心。只有用戶才能說清要用哪個(gè)地區(qū)顯示頁(yè)面。

但是謝天謝地,通常可以肯定地假設(shè)在使用應(yīng)用程序期間,用戶不會(huì)改變顯示語(yǔ)言。所以通常可以把地區(qū)與會(huì)話關(guān)聯(lián)起來。



回頁(yè)首


解決特定于地區(qū)的語(yǔ)言的顯示問題

對(duì)于 JSP 應(yīng)用程序,至少有兩種處理不同語(yǔ)言的顯示問題的普遍接受方法可以使用:

  • 存儲(chǔ)多組 JSP,每組 JSP 都用不同的語(yǔ)言編碼,然后根據(jù)用戶的地區(qū)選擇在這些 JSP 之間切換。
  • 分離所有使用的字符串,代之以從資源綁定獲得一個(gè)特定于地區(qū)的字符串。(這種方式使用了 J2SE 特定于地區(qū)的資源綁定處理方式。)

本文提供了示例代碼的兩個(gè)版本,分別對(duì)應(yīng)上面的兩種方法。示例應(yīng)用程序是一個(gè)叫做 developerWorks Email 的虛構(gòu)的電子郵件服務(wù)的登錄屏幕。首先,會(huì)用語(yǔ)言選擇屏幕提示電子郵件系統(tǒng)的用戶,讓他確定所需的當(dāng)前會(huì)話地區(qū)。可以進(jìn)行的選擇包括英語(yǔ)、韓語(yǔ)、日語(yǔ)和中文,如圖 3 所示。如果想試試代碼的效果,請(qǐng)用 http://<server address>/dwi18n/multdir/index.jsp 這個(gè) URL 訪問該頁(yè)面。

用圖片進(jìn)行初始的語(yǔ)言選擇

通常沒有可靠的方法可以預(yù)先知道什么樣的字體支持引入的客戶機(jī)。為了節(jié)約存儲(chǔ)和內(nèi)存,默認(rèn)情況下,大多數(shù)現(xiàn)代操作系統(tǒng)不會(huì)預(yù)先安裝對(duì)所有 Unicode 字符的所有字體的支持。有些瀏覽器會(huì)在第一次訪問包含必要字體的 Web 頁(yè)面時(shí),嘗試下載和安裝這些字體。用圖片來顯示外國(guó)字符或者國(guó)旗是表示語(yǔ)言選擇屏幕的一種可行方式。


圖 3. 進(jìn)行顯式地區(qū)選擇的語(yǔ)言選擇屏幕
四種不同的地區(qū)在同一臺(tái)機(jī)器上

在圖 3 中,語(yǔ)言選擇屏幕用 4 個(gè)圖片表示 4 個(gè)地區(qū)選擇(請(qǐng)參閱 Using images for initial language selection)。登錄屏幕使用用戶在這里選擇的語(yǔ)言進(jìn)行顯示。圖 4 顯示了日語(yǔ)的登錄屏幕。


圖 4. 日語(yǔ)登錄屏幕
日語(yǔ)登錄屏幕


回頁(yè)首


使用多組冗余的特定于語(yǔ)言的 JSP 集

示例的第一個(gè)版本位于示例代碼發(fā)行包的 webapps/dwi18n/multdir/ 目錄中,它運(yùn)用了多組 JSP 頁(yè)面。圖 5 顯示了這個(gè)應(yīng)用程序的目錄結(jié)構(gòu)。


圖 5. dwi18n/multdir 的目錄結(jié)構(gòu)顯示了特定于地區(qū)的目錄
dwi18n/multdir 的目錄結(jié)構(gòu)顯示了特定于地區(qū)的目錄

在圖 5 中,每個(gè)地區(qū)都有對(duì)應(yīng)的子目錄。與 en_EN 地區(qū)對(duì)應(yīng)的用英文編碼的 JSP 在 en 子目錄中。與 ko_KR 地區(qū)對(duì)應(yīng)的用韓語(yǔ)編碼的 JSP 在 ko 子目錄中。對(duì)于 ja_JP 地區(qū),用日語(yǔ)編碼的 JSP 在 ja 子目錄中。zh_CN 地區(qū)則用 zh 子目錄中用中文編碼的 JSP 表示。每個(gè)子目錄都既包含登錄屏幕的 JSP(login.jsp),也包含數(shù)據(jù)確認(rèn) JSP(confirm.jsp)。

數(shù)據(jù)確認(rèn) JSP 只顯示在這個(gè)簡(jiǎn)單的示例中輸入的數(shù)據(jù)。例如,如果在中文登錄屏幕中輸入了信息,然后單擊按鈕,那么數(shù)據(jù)確認(rèn) JSP 就會(huì)顯示輸入的數(shù)據(jù),如圖 6 所示。


圖 6. 中文的確認(rèn)頁(yè)面
中文的確認(rèn)頁(yè)面

編寫 JSP 代碼

地區(qū)選擇 JSP 叫做 index.jsp,它直接鏈接到特定于語(yǔ)言的一組 JSP。這個(gè)版本的 index.jsp 的代碼在清單 1 中:


清單 1. 地區(qū)選擇 JSP 直接鏈接到特定于語(yǔ)言的頁(yè)面

				
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Select a language</title>
</head>
<body>
<table>
<tr>
<td colspan=4 bgcolor="black">
<br/>
<center><font face="arial" size=+2 color="white">
    <b><i>developer</i>Works Email</b></font>
</center>
<br/>

</td>
</tr>
 <tr><td>
 <c:url value="en/login.jsp" var="englishURL"/>
 <a href="${englishURL}">
    <img src="english.gif"/>
 </a>
</td>
<td>
 <c:url value="ja/login.jsp" var="japaneseURL"/>
  <a href="${japaneseURL}">
       <img src="japanese.gif"/>
  </a>
</td>
<td>
  <c:url value="ko/login.jsp" var="koreanURL"/>
  <a href="${koreanURL}">
       <img src="korean.gif"/>
  </a>
</td>
<td>
  <c:url value="zh/login.jsp" var="chineseURL"/>
  <a href="${chineseURL}">
       <img src="chinese.gif"/>
  </a>
</td>
</tr>
</table>
</body>
</html>


注意,清單 1 中使用了來自 JSP 標(biāo)準(zhǔn)標(biāo)簽庫(kù)(JSTL)的 <c:url> 標(biāo)簽來創(chuàng)建鏈接 URL。這可以確保會(huì)話管理得到恰當(dāng)?shù)奶幚怼#ㄕ?qǐng)參閱參考資料,以了解關(guān)于 JSTL 和 <c:url> 標(biāo)簽的更多信息。)

每組 login.jsp 和 confirm.jsp 都用特定于地區(qū)的語(yǔ)言編寫代碼。清單 2 顯示了 ja_JP 地區(qū)的 login.jsp(與 圖 4 對(duì)應(yīng)):


清單 2. ja_JP 地區(qū)的登錄頁(yè)面(login.jsp)

				
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
<title>developerWorks 電子メール</title>
</head>
<body>
<c:url value="confirm.jsp" var="actionURL"/>
<form action="${actionURL}" method="post">
<table>
<tr>
<td colspan=2 bgcolor="black">
<br/>
<center><font face="arial" size=+2 color="white">
    <b><i>developer</i>Works 電子メール</b></font>
</center>
<br/>

</td>
</tr>

<tr>
<td>ユーザ ID</td>
<td><input type="text" name="userid" size="40"/></td>
</tr>

<tr>
<td>パスワード</td>
<td><input type="password" name="pass" size="40"/></td>
</tr>

<tr>
<td colspan="2" align="center">
<input type="submit" value="ログイン"/></td>
</tr>


</table>

</form>

</body>
</html>


與此類似,對(duì)于 zh_CN 地區(qū),confirm.jsp(與 圖 6 對(duì)應(yīng))是用中文編碼的,如清單 3 所示:


清單 3. zh_CN 地區(qū)的數(shù)據(jù)確認(rèn)頁(yè)面(confirm.jsp)

				
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
<title>developerWorks 電郵</title>
</head>
<body>

<table border="1">
<tr>
<td colspan=2 bgcolor="black">
<br/>
<center><font face="arial" size=+2 color="white">
     <b><i>developer</i>Works 電郵</b></font>
</center>
<br/>

</td>
</tr>


<tr>
<td>用戶帳號(hào)</td>
<td>${param.userid}</td>
</tr>

<tr>
<td>密碼</td>
<td>${param.pass}</td>
</tr>




</table>


</body>
</html>



前面介紹的多冗余集(multiple-redundant-set)方法對(duì)于以下這類應(yīng)用程序是一種可行的解決方案:

  • 主要用一種語(yǔ)言訪問,偶爾從其他地區(qū)訪問。
  • 底層表示層的 JSP 變化不是很頻繁。


回頁(yè)首


使用 J2SE 資源綁定

前面一節(jié) Using multiple redundant sets of language-specific JSPs 中的解決方案的最大不足在于:當(dāng)需要更新特定于語(yǔ)言的 JSP 集時(shí),所有冗余編碼的 JSP 集都必須同時(shí)更新。對(duì)于一個(gè)中等規(guī)模的項(xiàng)目而言,這會(huì)造成冗長(zhǎng)的、容易出錯(cuò)的更新。

而現(xiàn)在要介紹的解決方案的外觀和作用都與前一個(gè)類似,但是在這個(gè)案例中,只有一套 login.jsp 和 confirm.jsp。這個(gè)解決方案利用了 J2SE 在資源綁定中對(duì)地區(qū)的支持,只在需要的時(shí)候才采用特定于地區(qū)的文本字符串(請(qǐng)參閱參考資料,以了解更多關(guān)于 J2SE 資源綁定的信息)。這個(gè)解決方案的示例代碼位于 webapps\dwi18n\javares 目錄中。如果部署了示例代碼,那么請(qǐng)使用 http://<server address>/dwi18n/javares/index.jsp 這個(gè) URL。

圖 7 顯示了運(yùn)行在同一臺(tái)客戶端機(jī)器上的 4 個(gè)瀏覽器會(huì)話,每個(gè)會(huì)話請(qǐng)求的都是不同的地區(qū)。


圖 7. 同一臺(tái)機(jī)器上的 4 個(gè)不同地區(qū)
同一臺(tái)機(jī)器上的 4 個(gè)不同地區(qū)

在圖 7 中,可以清楚地看到國(guó)際化的 JSP 應(yīng)用程序可以同時(shí)處理多個(gè)地區(qū)。

編寫 JSP 代碼

在這個(gè)案例中,index.jsp 略有不同,因?yàn)樗F(xiàn)在鏈接到單獨(dú)的 login.jsp。清單 4 顯示了這個(gè)版本的 index.jsp 的代碼:


清單 4. 鏈接到單獨(dú) login.jsp 的地區(qū)選擇頁(yè)面

				
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Select Language</title>
</head>
<body>
<table>
<tr>
<td colspan=4 bgcolor="black">
<br/>
<center><font face="arial" size=+2 color="white">
    <b><i>developer</i>Works Email</b></font>
    </center>
<br/>

</td>
</tr>
 <tr><td>

 <c:url value="login.jsp" var="englishURL">
   <c:param name="locale" value="en_US"/>
 </c:url>

 <a href="${englishURL}">
    <img src="english.gif"/>
 </a>
</td>
<td>
  <c:url value="login.jsp" var="japaneseURL">
     <c:param name="locale" value="ja_JP"/>
  </c:url>

  <a href="${japaneseURL}">
       <img src="japanese.gif"/>
  </a>
</td>
<td>
  <c:url value="login.jsp" var="koreanURL">
     <c:param name="locale" value="ko_KR"/>
  </c:url>

  <a href="${koreanURL}">
       <img src="korean.gif"/>
  </a>
</td>
<td>
  <c:url value="login.jsp" var="chineseURL">
     <c:param name="locale" value="zh_CN"/>
  </c:url>

  <a href="${chineseURL}">
       <img src="chinese.gif"/>
  </a>
</td>
</tr>
</table>
</body>
</html>

注意,在清單 4 中,使用 JSTL 的 <c:param> 標(biāo)簽設(shè)置了一個(gè)叫做 locale 的 URL 請(qǐng)求參數(shù)。當(dāng)用戶單擊語(yǔ)言選擇時(shí),這個(gè)參數(shù)被傳遞給 login.jsp。清單 5 顯示了 login.jsp 的代碼:


清單 5. 使用 J2SE 資源綁定的登錄頁(yè)面(login.jsp)

				

<%@ page pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<c:set var="loc" value="en_US"/>
<c:if test="${!(empty param.locale)}">
  <c:set var="loc" value="${param.locale}"/>
</c:if>
<fmt:setLocale value="${loc}" />


<fmt:bundle basename="app">
<head>
<title>developerWorks <fmt:message key="email"/></title>
</head>
<body>

<c:url value="confirm.jsp" var="formActionURL" />

<form action="${formActionURL}" method="post">
<table>
<tr>
<td colspan=2 bgcolor="black">
<br/>
<center><font face="arial" size=+2 color="white"><b>
       <i>developer</i>Works <fmt:message key="email"/>
         </b></font></center>
<br/>

</td>
</tr>
<tr>
<td><fmt:message key="userid"/></td>
<td>

<input type="hidden" name="locale" value="${loc}"/>
<input type="text" name="userid" size="40"/></td>
</tr>

<tr>
<td><fmt:message key="password"/></td>
<td><input type="text" name="pass" size="40"/></td>
</tr>


<tr>
<td colspan="2" align="center">
<input type="submit" value="<fmt:message key='login'/>"/></td>
</tr>

</table>

</form>

</body>
</fmt:bundle>

</html>

清單 5 使用了 JSTL 國(guó)際化輔助標(biāo)簽庫(kù)(請(qǐng)參閱 參考資料)。如果傳入的 param.locale 為空,那么在默認(rèn)情況下,地區(qū)設(shè)置為 en_US。在使用資源綁定的時(shí)候,可以用 <fmt:setLocale> 標(biāo)簽設(shè)置地區(qū)。

完成地區(qū)設(shè)置之后,<fmt:message> 就會(huì)從處于綁定狀態(tài)的屬性文件中提取文本,該文本對(duì)應(yīng)于指定的關(guān)鍵字。用 <fmt:bundle> JSTL 標(biāo)簽把綁定的基本名稱設(shè)為 app。如果查看 dwi18n/WEB-INF/classes 目錄,可以看到資源綁定中的所有文件都在那里。表 1 描述了這些文件。請(qǐng)參閱參考資料,以獲得使用 J2SE 資源綁定的更多信息。

表 1. 資源綁定中的文件

文件名 說明
app.properties 默認(rèn)使用的屬性文件。對(duì)應(yīng) en_US 地區(qū)。
app_zh.properties zh_CN 地區(qū)的屬性文件。包含用中文編碼的字符串。
app_ko.properties ko_KR 地區(qū)的屬性文件。包含用韓語(yǔ)編碼的字符串。
app_ja.properties ja_JP 地區(qū)的屬性文件。包含用日語(yǔ)編碼的字符串。
*.ucd 創(chuàng)建屬性文件的 Unicode 源文件。
convacii.bat 將 ucd 文件轉(zhuǎn)換成屬性文件的批處理文件。

作為示例,清單 6 顯示了app_ko.properties 文件的內(nèi)容:


清單 6. 資源綁定中的 app_ko.properties 文件

				
email=\uc774\uba54\uc77c
userid=\uc544\uc774\ub514
password=\ube44\ubc00\ubc88\ud638
login=\ub85c\uadf8\uc778

注意,在清單 6 中,所有的 Unicode 字符均被轉(zhuǎn)義。必須這么做,因?yàn)?Java 的資源綁定機(jī)制只接受用 ASCII 編碼的屬性文件。要?jiǎng)?chuàng)建這個(gè)文件,既可以使用 IDE 中的字符串資源編輯器,也可以使用 Unicode 編輯器創(chuàng)建一個(gè) Unicode 文件,然后用 JDK 的 nativetoascii 工具轉(zhuǎn)換它。在該例中,convascii.bat 文件負(fù)責(zé)進(jìn)行轉(zhuǎn)換。



回頁(yè)首


結(jié)束語(yǔ)

在設(shè)置國(guó)際化 JSP 應(yīng)用程序時(shí),需要了解它的獨(dú)特需求。應(yīng)用程序必須做好準(zhǔn)備,支持具有不同地區(qū)需求的用戶的多個(gè)并發(fā)訪問。本文介紹了用特定于地區(qū)的語(yǔ)言文本顯示國(guó)際化應(yīng)用程序這個(gè)問題的兩個(gè)解決方案。但我也僅僅是觸及到創(chuàng)建國(guó)際化服務(wù)器端應(yīng)用程序的迷人藝術(shù)的表面。其他重要的問題包括處理不同的日期和貨幣格式、管理 GUI 布局和使用專門的輸入法編輯器(IME,輸入外國(guó)字符的實(shí)用軟件)。請(qǐng)參閱參考資料,以獲得有助于進(jìn)一步了解國(guó)際化的信息。




回頁(yè)首


下載

描述 文件名稱 文件大小  下載方法
Sample code for article tested on Tomcat 5.5.7 code.zip 25 KB  FTP
關(guān)于下載方法的信息 Get Adobe? Reader?


回頁(yè)首


參考資料

  • 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文

  • 單擊本文頂部或底部的 Code(或請(qǐng)參閱 Downloads)下載本文討論的代碼 code.zip。

  • 地區(qū)中使用的兩個(gè)字符的語(yǔ)言代碼是 ISO-639 代碼。Consult the list.

  • 地區(qū)中使用的兩個(gè)字符的國(guó)家代碼是 ISO-3166 代碼。Consult the list.

  • 要了解所有的 JSTL 標(biāo)簽,請(qǐng)參閱 developerWorks 上的 A JSTL 入門 系列。其中 表示就是一切 與這些標(biāo)簽緊密相關(guān),它介紹了一些國(guó)際化標(biāo)簽。

  • 通過閱讀 java.util.ResourceBundle 類的 Javadoc,發(fā)現(xiàn)更多關(guān)于 J2SE 資源綁定、如何創(chuàng)建它們和在哪兒放置它們的消息。也可以閱讀 javadoc 的在線文章 here

  • 教程 Java internationalization basics(developerWorks,2002 年 4 月)是學(xué)習(xí)所有可用的 J2SE 國(guó)際化支持機(jī)制的一個(gè)好地方,允許您創(chuàng)造性地在服務(wù)器端應(yīng)用程序中應(yīng)用它們。

  • Unicode Input Method Editor 是一個(gè)可以協(xié)助盡早在開發(fā)周期中發(fā)現(xiàn)全球化問題的工具,它能提供了一個(gè)簡(jiǎn)單的機(jī)制,以便很容易地重現(xiàn)全球化問題。

  • International Components for Unicode 是 Unicode 支持、軟件國(guó)際化、全球化的一個(gè)成熟的、廣泛應(yīng)用的庫(kù)。ICU 是一個(gè)由 IBM 贊助、支持和使用的開源開發(fā)項(xiàng)目。

  • 參閱 Sing Li 撰寫的書籍 Beginning JavaServer Pages(John Wiley & Sons,2005),以便進(jìn)一步研究國(guó)際化和本地化。

  • 通過參與 developerWorks blogs 加入 developerWorks 社區(qū)。

  • 可以在 developerWorks 的 Java 技術(shù)專區(qū) 發(fā)現(xiàn)關(guān)于 Java 編程的各個(gè)方面的文章。



  • 請(qǐng)參閱 Developer Bookstore,以獲得技術(shù)書籍的完整清單,其中包括數(shù)百本Java 相關(guān)主題的書籍。


回頁(yè)首


關(guān)于作者

Author photo

Sing Li 是一位顧問和自由作者。他的著作包括 Beginning JavaServer Pages Professional Apache Tomcat 5 Pro JSP - Third EditionEarly Adopter JXTAProfessional Jini,以及其他大量書籍。他還是技術(shù)雜志的定期投稿人,也是 P2P 革命的積極倡導(dǎo)者。您可以通過 westmakaha@yahoo.com 與他聯(lián)系。



黑咖啡 2006-02-13 14:10 發(fā)表評(píng)論
]]>
用struts框架嘗試國(guó)際化程序?qū)崿F(xiàn)http://www.tkk7.com/faithwind/articles/30441.html黑咖啡黑咖啡Mon, 13 Feb 2006 05:45:00 GMThttp://www.tkk7.com/faithwind/articles/30441.htmlhttp://www.tkk7.com/faithwind/comments/30441.htmlhttp://www.tkk7.com/faithwind/articles/30441.html#Feedback0http://www.tkk7.com/faithwind/comments/commentRss/30441.htmlhttp://www.tkk7.com/faithwind/services/trackbacks/30441.html

用struts框架嘗試國(guó)際化程序?qū)崿F(xiàn) 


 


 

 

struts是一個(gè)MVC框架,據(jù)說struts可以輕松實(shí)現(xiàn)國(guó)際化;于是根據(jù)網(wǎng)上的資料,做了一個(gè)嘗試,因?yàn)榈谝淮巫龆嗾Z(yǔ)言程序,還是拐了很多彎路;但所幸,經(jīng)過不斷的嘗試,終于成功的實(shí)現(xiàn)多語(yǔ)言版本的簡(jiǎn)單頁(yè)面;

因?yàn)槌绦蚍浅:?jiǎn)單,所以在整個(gè)嘗試過程中,全部使用手工編碼,沒有使用任何輔助工具;

1、 建立服務(wù)器

我使用Tomcat4作為測(cè)試環(huán)境,建立過程(略);

2、 下載struts

可以到http://jakarta.apache.org/struts/index.html下載,下載后解壓,把其中的.war文件拷貝到Tomcat的webapps目錄下,啟動(dòng)Tomcat,如果http://localhost:8080/struts-example/ 運(yùn)行沒有問題,說明環(huán)境建立成功;這些.war文件在Tomcat啟動(dòng)后會(huì)自動(dòng)展開成文件,里面有源代碼,可以作為源碼研究;

3、 建立工程

在webapps目錄下建立一個(gè)international文件夾,再在international目錄下建立WEB-INF文件夾和WEB-INF/classes文件夾,這些都是一個(gè)JSP工程必須的;

4、 加了struts的類

在WEB-INF目錄下建立一個(gè)lib子目錄,把struts-example\WEB-INF\lib目錄下將所有.jar文件拷貝到該目錄下;這些文件是struts的控制類庫(kù)和標(biāo)簽類庫(kù)等;

commons-beanutils.jar 
commons-collections.jar 
commons-digester.jar 
commons-fileupload.jar 
commons-lang.jar 
commons-logging.jar 
commons-validator.jar 
jakarta-oro.jar 
struts.jar


5、 加入struts標(biāo)簽定義文件

從struts-example\WEB-INF目錄下,把.TLD文件拷貝到international的WEB-INF目錄下,這些文件標(biāo)簽庫(kù)的定義文件;

struts-bean.tld 
struts-html.tld 
struts-logic.tld 
struts-nested.tld 
struts-template.tld 
struts-tiles.tld


6、 建立struts的config文件

建立struts的config文件的struts-config.xml,內(nèi)容如下:

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE struts-config PUBLIC 
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" 
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd";> 
<struts-config> 
<message-resources parameter="resources.application"/> 
</struts-config>


message-resources標(biāo)簽是指message資源的文件,就是我們存放我們的多種語(yǔ)言的提示信息的文件,resources.application表是classes目錄下的resources目錄用來存放資源文件,默認(rèn)語(yǔ)言文件名為application.properties,中文為application_zh_CN.properties,其他語(yǔ)言類似;

7、 建立web.xml文件

<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE web-app 
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" 
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd";> 
<web-app> 
<display-name>test international</display-name> 
<servlet> 
<servlet-name>action</servlet-name> 
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class> 
<init-param> 
<param-name>config</param-name> 
<param-value>/WEB-INF/struts-config.xml</param-value> 
</init-param> 
<init-param> 
<param-name>debug</param-name> 
<param-value>2</param-value> 
</init-param> 
<init-param> 
<param-name>detail</param-name> 
<param-value>2</param-value> 
</init-param> 
<load-on-startup>2</load-on-startup> 
</servlet> 
<servlet-mapping> 
<servlet-name>action</servlet-name> 
<url-pattern>*.do</url-pattern> 
</servlet-mapping> 
<welcome-file-list> 
<welcome-file>index.jsp</welcome-file> 
</welcome-file-list> 
<taglib> 
<taglib-uri>/tags/struts-bean</taglib-uri> 
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location> 
</taglib> 
<taglib> 
<taglib-uri>/tags/struts-html</taglib-uri> 
<taglib-location>/WEB-INF/struts-html.tld</taglib-location> 
</taglib> 
<taglib> 
<taglib-uri>/tags/struts-logic</taglib-uri> 
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location> 
</taglib> 
<taglib> 
<taglib-uri>/tags/struts-nested</taglib-uri> 
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location> 
</taglib> 
<taglib> 
<taglib-uri>/tags/struts-tiles</taglib-uri> 
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location> 
</taglib> 
</web-app>


上面的web.xml定義了struts的控制類、config文件和標(biāo)簽,因?yàn)楸容^簡(jiǎn)單,所以不做解釋;

8、 建立資源文件

在classes目錄下,建立一個(gè)resources目錄,用來存放資源文件;

先建立默認(rèn)的資源文件application.properties和英文(美國(guó))的資源文件application_en_US.properties,內(nèi)容為:

# -- international test -- 
test.title=international application test 
test.body=This is a international application test


先建立這兩個(gè)文件,中文的等下一步建立

9、建立jsp文件

在international目錄下,建立index.jsp文件,內(nèi)容為:

<%@ page contentType="text/html;charset=UTF-8" %> 
<%@ taglib uri="/tags/struts-bean" prefix="bean" %> 
<%@ taglib uri="/tags/struts-html" prefix="html" %> 
<%@ taglib uri="/tags/struts-logic" prefix="logic" %> 
<html:html locale="true"> 
<head> 
<title><bean:message key="test.title"/></title> 
<html:base/> 
</head> 
<body bgcolor="white"> 
<p><bean:message key="test.body"/></p> 
</body> 
</html:html>


在這里表示使用瀏覽器默認(rèn)的地區(qū)和語(yǔ)言;key的意思是取對(duì)應(yīng)資源文件里的test.title項(xiàng)目的內(nèi)容;

啟動(dòng)Tomcat,在瀏覽器里輸入http://localhost:8080/international/,查看效果,如果瀏覽器標(biāo)題顯示international application test,頁(yè)面里顯示This is a international application test則說明你的程序成功了;下面只要增加資源文件,你就可以在多種語(yǔ)言的系統(tǒng)里看了;

10、 建立簡(jiǎn)體中文的資源文件

在resources目錄下建立一個(gè)application_cn.properties,輸入內(nèi)容:

# -- international test -- 
test.title=國(guó)際化程序測(cè)試 
test.body=這是一個(gè)國(guó)際化程序測(cè)試?yán)?/CCID_CODE>


因?yàn)閖ava的國(guó)際化是通過unicode碼來實(shí)現(xiàn),所以要把代碼轉(zhuǎn)為unicode碼;在Dos下,轉(zhuǎn)到resources目錄,執(zhí)行:

native2ascii application_cn.properties application_zh_CN.properties


轉(zhuǎn)換后的application_zh_CN.properties文件內(nèi)容為:

# -- international test -- 
test.title=\u56fd\u9645\u5316\u7a0b\u5e8f\u6d4b\u8bd5 
test.body=\u8fd9\u662f\u4e00\u4e2a\u56fd\u9645\u5316\u7a0b\u5e8f\u6d4b\u8bd5\u4f8b\u5b50


這就是上面的中文的unicode碼;

重新啟動(dòng)Tomcat, 在瀏覽器里輸入http://localhost:8080/international/,你看,標(biāo)題和內(nèi)容是不是變成中文了;

11、 建立繁體中文的資源文件

在resources目錄下建立一個(gè)application_tw.properties,輸入內(nèi)容:

# -- international test -- 
test.title=???H化程式?y?? 
test.body=這是一個(gè)國(guó)際化程式測(cè)試?yán)?/CCID_CODE>


因?yàn)閖ava的國(guó)際化是通過unicode碼來實(shí)現(xiàn),所以要把代碼轉(zhuǎn)為unicode碼;在Dos下,轉(zhuǎn)到resources目錄,執(zhí)行:

native2ascii application_tw.properties application_zh_TW.properties


轉(zhuǎn)換后的application_zh_TW.properties文件內(nèi)容為:

# -- international test -- 
test.title=\u570b\u969b\u5316\u7a0b\u5f0f\u6e2c\u8a66 
test.body=\u9019\u662f\u4e00\u500b\u570b\u969b\u5316\u7a0b\u5f0f\u6e2c\u8a66\u4f8b\u5b50


這就是上面的繁體中文的unicode碼;

12、 測(cè)試多語(yǔ)言

打開IE的“工具”->“Internet選項(xiàng)”菜單,“常規(guī)”選項(xiàng)卡,點(diǎn)擊其中的“語(yǔ)言”按鈕,添加“英語(yǔ)(美國(guó))-[en-us]”語(yǔ)言,將其他的語(yǔ)言刪除,重新啟動(dòng)IE后,輸入http://localhost:8080/international/index.jsp,你會(huì)發(fā)現(xiàn)內(nèi)容已經(jīng)變成英文;

用同樣的方法,可以測(cè)試簡(jiǎn)體中文和繁體中文。


黑咖啡 2006-02-13 13:45 發(fā)表評(píng)論
]]>
用AJAX開發(fā)智能Web應(yīng)用程序之高級(jí)篇 http://www.tkk7.com/faithwind/articles/30022.html黑咖啡黑咖啡Thu, 09 Feb 2006 05:36:00 GMThttp://www.tkk7.com/faithwind/articles/30022.htmlhttp://www.tkk7.com/faithwind/comments/30022.htmlhttp://www.tkk7.com/faithwind/articles/30022.html#Feedback0http://www.tkk7.com/faithwind/comments/commentRss/30022.htmlhttp://www.tkk7.com/faithwind/services/trackbacks/30022.html下載本文源代碼

  一、 引言

  在第一部分中,我們討論了AJAX基礎(chǔ)——建立從腳本到服務(wù)器的通訊的能力,這正是使HTML頁(yè)面具有動(dòng)態(tài)能力的原因所在。然而,這就意味著我們已準(zhǔn)備好拋棄我們自己版本的Yahoo郵件嗎?不,還沒有。原因在于:AJAX是一個(gè)混合的祝福。一方面,它使我們能夠在Web上創(chuàng)建豐富的桌面級(jí)的應(yīng)用程序;另一方面,如果我們把"翻頁(yè)面式"的Web應(yīng)用程序與客戶端/服務(wù)器或Swing版本的程序進(jìn)行比較,那么會(huì)看到其開發(fā)實(shí)踐并不很相同。我們將需要習(xí)慣于這樣的事實(shí):構(gòu)建一個(gè)豐富的UI需要時(shí)間。須知,允許用戶實(shí)現(xiàn)更大的靈活性也就相應(yīng)地需要付出更多的時(shí)間為代價(jià)。

  最后的答案當(dāng)然要依賴于大量的組件庫(kù)、框架以及具有工業(yè)力量的開發(fā)工具。且不考慮工具,本文集中于討論在今天對(duì)于AJAX熱心者有哪些技術(shù)是可用的。在強(qiáng)調(diào)需要構(gòu)建可重用的商業(yè)組件的同時(shí),本文將重點(diǎn)分析"隱含的"JavaScript中的面向?qū)ο蟮牧α俊A硗猓趶?qiáng)調(diào)需要構(gòu)建定制的UI組件的同時(shí),本文將介紹一個(gè)簡(jiǎn)便的方法——用定制的客戶端HTML標(biāo)簽來封裝描述邏輯。

  二、 AJAX語(yǔ)言——對(duì)象面向的JavaScript

  由定義來看,JavaScript是典型的AJAX語(yǔ)言。不同于Java,JavaScript并不強(qiáng)調(diào)OO風(fēng)格的編碼。然而,令人吃驚的是JavaScript居然全面支持所有的OO語(yǔ)言的主要屬性:封裝、繼承和多態(tài)性。Douglas Crockford甚至稱JavaScript是"世界上最易被誤解的編程語(yǔ)言"。讓我們回顧一下JavaScript的面向?qū)ο蟮牡胤桨伞?BR>
  數(shù)據(jù)類型

  在Java中,一個(gè)類定義了一個(gè)數(shù)據(jù)和它的相關(guān)行為的組合。盡管JavaScript保留了class關(guān)鍵字,但是它不支持與常規(guī)OOP語(yǔ)言一樣的語(yǔ)義。

  這聽起來可能覺得奇怪,但是在JavaScript中,對(duì)象是用函數(shù)來定義的。事實(shí)上,通過在下面的示例中定義一個(gè)函數(shù),你就定義了一個(gè)簡(jiǎn)單的空類Calculator:

function Calculator() {}

  一個(gè)新的實(shí)例的創(chuàng)建與在Java中相同-使用new操作符:

var myCalculator = new Calculator();

  上面這個(gè)函數(shù)不僅定義一個(gè)類,而且還擔(dān)當(dāng)了一個(gè)構(gòu)造器。在此,操作符new實(shí)現(xiàn)了這一魔術(shù)-實(shí)例化一個(gè)類Calculator的對(duì)象并且返回一個(gè)對(duì)象參考而不是只調(diào)用該函數(shù)。

  創(chuàng)建這樣的空類是沒錯(cuò),但在實(shí)際中并沒有多大用處。下面,我們準(zhǔn)備使用一個(gè)Java-腳本原型結(jié)構(gòu)來填充類定義。JavaScript使用原型當(dāng)作創(chuàng)建對(duì)象的模板。所有的原型屬性和方法被參考引用地復(fù)制到一個(gè)類的每個(gè)對(duì)象中,所以它們都具有相同的值。你可以改變一個(gè)對(duì)象中的原型屬性的值,并且該新值會(huì)覆蓋從原型中復(fù)制過來的缺省值,但是這僅對(duì)于在一個(gè)實(shí)例中。下列語(yǔ)句將把一個(gè)新屬性添加到Calculator對(duì)象的原型上:

Calculator.prototype._prop = 0;

  既然JavaScript并沒有提供一個(gè)方法來從句法上表示一個(gè)類定義,那么我們將使用with語(yǔ)句來標(biāo)記該類的定義邊界。這也將使得示例代碼更為短小,因?yàn)樵搘ith語(yǔ)句被允許在一個(gè)指定的對(duì)象上執(zhí)行一系列的語(yǔ)句而不需要限制屬性。

function Calculator() {};
with (Calculator) {
 prototype._prop = 0;
 prototype.setProp = function(p) {_prop = p};
 prototype.getProp = function() {return _prop};
}

  到目前為止,我們定義了并且初始化了公共變量_prop,并且為它提供了getter和setter方法。

  需要定義一個(gè)靜態(tài)變量?你可以把靜態(tài)變量當(dāng)作是為類所擁有的一個(gè)變量。因?yàn)樵贘avaScript中的類用函數(shù)對(duì)象來描述,所以我們只需要把一個(gè)新屬性添加到該函數(shù)上:

Calculator.iCount=0;

  現(xiàn)在,既然這個(gè)iCount變量是一個(gè)Calculator對(duì)象的屬性,那么它將會(huì)被類Calculator的所有實(shí)例所共享。

function Calculator() {Calculator.iCount++;};

  上面的代碼計(jì)算類Calculator的所有實(shí)例的個(gè)數(shù)。

  封裝

  通過使用如上面所定義的"Calculator",我們可以存取所有的"class"數(shù)據(jù);然而,這增加了派生類中命名沖突的危險(xiǎn)性。我們明顯地需要封裝以把對(duì)象看作自包含的實(shí)體。

  數(shù)據(jù)封裝的一種標(biāo)準(zhǔn)語(yǔ)言機(jī)制是使用私有變量。并且一個(gè)常用的仿效一個(gè)私有變量的JavaScript技術(shù)是在構(gòu)造器中定義一個(gè)局部變量;這樣以來,該局部變量的存取只能經(jīng)由getter和setter來實(shí)現(xiàn)-它們是該構(gòu)造器中的內(nèi)部函數(shù)。在下列實(shí)例中,_prop變量在Calculator函數(shù)中定義并且在函數(shù)范圍外不可見。其中有兩個(gè)匿名的內(nèi)部函數(shù)(分別被賦予setProp和getProp屬性)讓我們存取"私有"變量。另外,請(qǐng)注意,這里this的使用-十分相似于在Java中的用法:

function Calculator() {
 var _prop = 0;
 this.setProp = function (p){_prop = p};
 this.getProp = function() {return _prop};
};

  常常被忽視的是在JavaScript中作如此封裝所付出的代價(jià)。須知,這種代價(jià)可能是巨大的,因?yàn)閮?nèi)部函數(shù)對(duì)象對(duì)于該"class"的每一個(gè)實(shí)例被不斷地重復(fù)創(chuàng)建。

  因此,既然基于原型構(gòu)建對(duì)象速度更快并且消費(fèi)更少些的內(nèi)存,那么我們?cè)谧顝?qiáng)調(diào)性能的場(chǎng)所特別支持使用公共的變量。請(qǐng)注意,你可以使用命名慣例來避免名稱沖突-例如,在公共的變量的前面加上該類名。
繼承

  乍看之下,JavaScript缺乏對(duì)類層次的支持,這很相似于面向?qū)ο笳Z(yǔ)言的程序員對(duì)于現(xiàn)代語(yǔ)言的期盼。然而,盡管JavaScript句法沒有象Java一樣支持類繼承,但是我們?nèi)匀荒軌蛟贘avaScript中實(shí)現(xiàn)繼承-通過把已定義類的一個(gè)實(shí)例拷貝到其派生類的原型當(dāng)中。

  在我們提供舉例之前,我們需要介紹一個(gè)constructor屬性。JavaScript保證每一個(gè)原型中包含constructor-它擁有到該構(gòu)造器函數(shù)的一個(gè)參考。換句話說,Calculator.prototype.constructor包含一個(gè)到Calculator()的參考。

  現(xiàn)在,下面的代碼顯示了怎樣從基類Calculator派生類ArithmeticCalculator。其中,"第一行"取得類Calculator的所有的屬性,而"第二行"把原型constructor的值恢復(fù)成ArithmeticCalculator:

function ArithmeticCalculator() { };
with (ArithmeticCalculator) {
 ArithmeticCalculator .prototype = new Calculator();//第一行
 prototype.constructor = ArithmeticCalculator;//第二行
}

  就算上面的實(shí)例看起來象一個(gè)合成體而不象是繼承,但是JavaScript引擎還是清楚這個(gè)原型鏈的。特別是,instanceof操作符會(huì)正確地適用于基類和派生類。假定你創(chuàng)建類ArithmeticCalculator的一個(gè)新實(shí)例:

var c = new ArithmeticCalculator;

  表達(dá)式c instanceof Calculator和c instanceof ArithmeticCalculator都會(huì)成立。

  注意,在上面示例中的基類的constructor是在初始化ArithmeticCalculator原型時(shí)被調(diào)用的,而在創(chuàng)建派生類的實(shí)例時(shí)是不被調(diào)用的。這可能會(huì)帶來不想要的負(fù)面影響,而且為了實(shí)現(xiàn)初始化你應(yīng)該考慮創(chuàng)建一個(gè)獨(dú)立的函數(shù)。由于該構(gòu)造器并不是一個(gè)成員函數(shù),所以它無法通過this參考引用調(diào)用。我們將需要一個(gè)能調(diào)用超類的"Calculator"成員函數(shù):

function Calculator(ops) { ...};
with (Calculator) { prototype.Calculator=Calculator;}

  現(xiàn)在,我們可以寫一個(gè)繼承類-它顯式地調(diào)用基類的構(gòu)造器:

function ArithmeticCalculator(ops) { this.Calculator(ops);};
with (ArithmeticCalculator) {
 ArithmeticCalculator .prototype = new Calculator;
 prototype.constructor = ArithmeticCalculator;
 prototype.ArithmeticCalculator = ArithmeticCalculator;
}

  多態(tài)性

  JavaScript是一種非類型化的語(yǔ)言-在此,一切都是對(duì)象。因此,如果有兩個(gè)類A和B,它們都定義一個(gè)foo(),那么JavaScript將允許在A和B的實(shí)例上多態(tài)地調(diào)用foo()-即使不存在層次關(guān)系(雖然是可實(shí)現(xiàn)的)。從這一角度來看,JavaScript提供一個(gè)比Java更寬的多態(tài)性。這種靈活性,象往常一樣,也要付出代價(jià)。在這種情況中,代價(jià)是把類型檢查工作代理到應(yīng)用程序代碼。具體地說,如果需要檢查一個(gè)參考確實(shí)指向一個(gè)所希望的基類,那么這可以通過instanceof操作符來實(shí)現(xiàn)。

  另一方面,JavaScript并不檢查函數(shù)調(diào)用中的參數(shù)-這可以防止用一樣的命名和不同的參數(shù)來定義多態(tài)函數(shù)(并且讓編譯器選擇正確的簽名)。代之的是,JavaScript提供了一個(gè)Java 5風(fēng)格的函數(shù)范圍內(nèi)的argument對(duì)象-它允許你根據(jù)參數(shù)的類型和數(shù)量的不同而實(shí)現(xiàn)一個(gè)不同的行為。
三、 示例展示

  本文所附源碼列表1實(shí)現(xiàn)了一個(gè)計(jì)算器-它可以計(jì)算以一個(gè)逆向波蘭式標(biāo)志的表達(dá)式。該示例展示了本文中所介紹的主要技術(shù)并且也介紹了一些獨(dú)特的JavaScript特性的用法,例如在一個(gè)動(dòng)態(tài)函數(shù)調(diào)用中以一個(gè)數(shù)組元素的方式訪問對(duì)象屬性。

  為了使列表1工作,我們需要另外準(zhǔn)備一些代碼-它們用于實(shí)例化該計(jì)算器對(duì)象并且調(diào)用evaluate方法:

var e = new ArithmeticCalcuator([2,2,5,"add","mul"]);
alert(e.evaluate());

  四、 AJAX組件授權(quán)

  所有的AJAX組件授權(quán)方案在今天被邏輯地分成兩組。具體地說,第一組用于與基于HTML的UI定義的無縫集成。第二組把HTML當(dāng)作一個(gè)UI定義語(yǔ)言以支持某種XML。在本文中,我們從第一組中來展示一種方法-雖然它存在于瀏覽器之中卻是類似于JSP標(biāo)簽。這些瀏覽器特定的組件授權(quán)擴(kuò)展在IE情形下稱作元素行為,而在最近版本的Firefox,Mozilla和Netscape 8情形下稱作可擴(kuò)展的綁定。

  五、 定制標(biāo)簽

  Internet Explorer,從版本5.5開始,支持定制的客戶端HTML元素的JavaScript授權(quán)。不象JSP標(biāo)簽,這些對(duì)象并沒有在服務(wù)器端被預(yù)處理到HTML中。而是,它們成為一標(biāo)準(zhǔn)HTML對(duì)象模型的合法擴(kuò)展,并且包括構(gòu)造控件在內(nèi)的一切事情,都是動(dòng)態(tài)地發(fā)生在客戶端的。同樣,基于Gecko-引擎的瀏覽器能夠用一個(gè)可重用功能動(dòng)態(tài)地裝飾任何現(xiàn)有的HTML元素。

  因此,我們有可能用具有HTML語(yǔ)法的方法、事件和屬性來構(gòu)建一個(gè)具有豐富的UI組件的庫(kù)。這樣的組件可以被自由地混合于標(biāo)準(zhǔn)HTML中。內(nèi)部地,這些組件將會(huì)與應(yīng)用程序服務(wù)器進(jìn)行通訊-以AJAX風(fēng)格。換句話說,你有可能(并且相對(duì)簡(jiǎn)單地)構(gòu)建自己的AJAX對(duì)象模型。
這種IE風(fēng)味的方法被稱為HTC或HTML組件;其Gecko版本被稱為XBL-可擴(kuò)展的綁定語(yǔ)言(eXtensible Bindings Language)。為了實(shí)現(xiàn)本文目的,我們集中于討論IE。

  六、 輸入HTML組件-HTC

  HTC或HTML組件也被稱作行為。它們被劃分為兩種類型:一種是依附的行為-用一組屬性、事件和方法裝飾任何現(xiàn)有的HTML元素;另一種是元素行為-看上去象宿主頁(yè)面的定制的HTML標(biāo)簽的一個(gè)擴(kuò)展集合。依附的行為和元素行為一起提供了開發(fā)組件和應(yīng)用程序的一種簡(jiǎn)單方案。在此,我們將展示一下最為綜合的情形-元素行為。

  數(shù)據(jù)綁定復(fù)選框控件

  為了展示元素行為,我們將構(gòu)建一個(gè)定制的數(shù)據(jù)綁定復(fù)選框。構(gòu)建這樣一個(gè)控件背后的基本原因在于,一個(gè)標(biāo)準(zhǔn)HTML復(fù)選框具有下面若干顯著的缺點(diǎn):

  ·需要應(yīng)用程序編碼來把"checked"屬性的值映射到商業(yè)域值,例如"Y[es]"/"N[o]","M[ale]"/"F[emale]",等等。HTML復(fù)選框使用"checked"屬性,而許多其它HTML控件使用的則是"value"屬性。

  ·需要應(yīng)用程序編碼來維持該控件的狀態(tài)(修改過的/未修改過的)。這實(shí)際上是在所有的HTML控件普遍存在的一個(gè)問題。

  ·需要應(yīng)用程序編碼才能創(chuàng)建一個(gè)關(guān)聯(lián)標(biāo)簽-它應(yīng)該接受鼠標(biāo)點(diǎn)擊并相應(yīng)地改變?cè)搹?fù)選框的狀態(tài)。

  ·標(biāo)準(zhǔn)HTML復(fù)選框不支持"校驗(yàn)"事件以允許取消一個(gè)GUI行為,而這種要求可能存在于某些應(yīng)用程序中。

  現(xiàn)在,讓我們看一個(gè)正在構(gòu)建的該控件的用法示例,它的用法可能如下情形:

<checkbox id="cbx_1" value="N" labelonleft="true"
label="Show Details:" onValue="Y" offValue="N"/>

  另外,我們的控件將支持可取消的事件onItemChanging和通知事件onItemChanged。

  定義定制標(biāo)簽

  從結(jié)構(gòu)上講,一個(gè)定制標(biāo)簽是一個(gè)具有一個(gè)HTC擴(kuò)展名的文件-它在<PUBLIC:COMPONENT>和</PUBLIC:COMPONENT>標(biāo)志之間對(duì)它的屬性,方法和事件加以描述。

  為了定義一個(gè)定制CHECKBOX標(biāo)簽,我們創(chuàng)建一個(gè)如下列代碼片斷中的文件checkbox.htc-其中,第一行負(fù)責(zé)設(shè)置該組件的標(biāo)簽名:

<PUBLIC:COMPONENT NAME="cbx" tagName="CHECKBOX">
<PROPERTY NAME="value" GET="getValue" PUT="putValue" />
//我們把組件的所有另外的屬性放在這里
<METHOD NAME="show" />
//我們把組件的所有另外的方法放在這里
<EVENT NAME="onItemChanging" ID="onItemChanging"/>
//我們把組件將向應(yīng)用程序激活的所有另外的事件放在這里
<ATTACH EVENT="oncontentready" HANDLER="constructor" />
//我們把組件自己處理的另外的事件放在這里
<SCRIPT>
//我們把所有的方法,屬性getters和setters和事件處理器放在這里
</SCRIPT>
</PUBLIC:COMPONENT>

  使用定制標(biāo)簽

  盡管HTC文件的內(nèi)容比較重要,但是這與其文件名是什么無關(guān)。值得注意的是,指向該HTC文件的URL需要被使用IMPORT指令指定-這必須在相應(yīng)的定制標(biāo)簽第一次出現(xiàn)之前(在頁(yè)面上)完成。下面是最簡(jiǎn)單的可能的頁(yè)面使用一個(gè)定制的復(fù)選框可能看上去的樣子-假定該頁(yè)面和HTC文件處理同一個(gè)文件夾下:

<HTML xmlns:myns>
<?IMPORT namespace="myns" implementation="checkbox.htc" >
<BODY>
<myns:checkbox id='cbx_1' label='Hello'/>
</BODY>
</HTML>

  請(qǐng)注意,定制CHECKBOX是怎樣在打開的HTML標(biāo)簽中被映射到一個(gè)非缺省的命名空間"myns"的。這個(gè)IMPORT指令實(shí)現(xiàn)把HTC同步加載到瀏覽器的內(nèi)存并且還指示瀏覽器怎樣為適當(dāng)?shù)拿臻g實(shí)現(xiàn)名稱確定的(HTC到命名空間的關(guān)聯(lián)可能是多對(duì)一的)。

  定制標(biāo)簽的構(gòu)造器

  最好的初始化HTC的方法是,一旦它被裝載就處理oncontentready事件。因此,我們可以定義處理器函數(shù)-為了概念清晰起見,我們稱之為構(gòu)造器:

<ATTACH EVENT="oncontentready" HANDLER="constructor" />

  constructor()的邏輯是簡(jiǎn)單的:根據(jù)屬性labelonleft的值(見下面的屬性定義)按順序連接一個(gè)常規(guī)HTML復(fù)選框和HTML標(biāo)簽:

function constructor() {
 //我們將把一個(gè)HTML復(fù)選框和標(biāo)簽添加到元素體
 //詳細(xì)情形見列表2
}

  定義定制標(biāo)簽屬性

  為了定義屬性labelonleft,我們又在<PUBLIC:COMPONENT>部分加上一行:

<PROPERTY NAME="labelonleft" VALUE="true"/>

  請(qǐng)注意,這個(gè)屬性并沒有包含getter和/或setter方法。屬性onValue和offValue不僅提供了從復(fù)選框狀態(tài)到一個(gè)商業(yè)值域的映射而且不需要getters和setters:

<PROPERTY NAME="onValue" VALUE="true"/>
<PROPERTY NAME="offValue" VALUE="false" />

  然而,屬性checked是用兩個(gè)getter和setter定義的:

<PROPERTY NAME="checked" GET="getChecked" PUT="putChecked" />

  因此,我們?cè)冢糞CRIPT>部分建立了上面兩個(gè)方法的定義。正如你所見,setter putChecked()-將在每次復(fù)選框狀態(tài)改變時(shí)激發(fā)-把value屬性設(shè)置為下面兩個(gè)變體之一:onValue或OffValue。請(qǐng)注意,putChecked()將不僅可由在復(fù)選框-宿主頁(yè)面中的腳本觸發(fā),而且也能通過在checkbox.htc中的相應(yīng)的任何賦值操作觸發(fā)。

var _value;
function putChecked( newValue ) {
 value = (newValue?onValue:offValue);
}
function getChecked(){
 return ( _value == onValue);
}

七、 為定制標(biāo)簽定義事件

  讓我們看一下onItemChanging和onItemChanged事件的定義以及這些事件是怎樣在value屬性的setter內(nèi)部被激發(fā)和處理的(見所附源碼中的列表2)。方法putValue()有幾個(gè)讓人感興趣的地方。首先,在分析CHECKBOX標(biāo)簽期間,可以調(diào)用這個(gè)方法-只要指定這個(gè)HTMLvalue屬性。這正解釋了為什么我們?yōu)榉菢?gòu)造對(duì)象建立一個(gè)單獨(dú)的邏輯分支-為把構(gòu)造過程與一個(gè)對(duì)用戶擊鍵的反應(yīng)區(qū)別開來。其次,在此我們展示了定制事件onItemChanging的創(chuàng)建和處理-它允許應(yīng)用程序取消行為。請(qǐng)注意,通過這種方式,無論是擊鍵還是通過編程方式實(shí)現(xiàn)賦值都能達(dá)到取消的目的。

  事件取消

  為了取消事件,一個(gè)應(yīng)用程序應(yīng)該攔截該事件并且把event.returnValue設(shè)置為false。下面的代碼片斷展示了應(yīng)用程序是怎樣實(shí)現(xiàn)取消事件過程的:

cbx_1::onItemChanging() {
. . . . .
if (canNotBeAllowed) {
 event.returnValue=false;
 . . . . .
}

  如果事件沒被取消,putValue()把內(nèi)部的普通HTML復(fù)選框的checked屬性設(shè)置為每個(gè)相應(yīng)的當(dāng)前值-如果它等于onValue,這個(gè)內(nèi)部復(fù)選框?qū)⒈贿x中;如果它等于offValue(不存在第三種選擇),復(fù)選框不被選中(完整的列表見本文所附源碼中的列表2)。

  復(fù)選框的HTML內(nèi)幕

  我們控件的繪制是通過助理函數(shù)addLabel()和addCheckBox()來實(shí)現(xiàn)的并且從一個(gè)constructor()內(nèi)部調(diào)用。這些函數(shù)把HTML注入進(jìn)元素的innerHTML。這種注入式HTML的一種簡(jiǎn)化形式如下所示:

<LABEL for=cb_{uniqueID}>Show Details:</LABEL>
<INPUT id=cb_{uniqueID} type=checkbox />

  在此,uniqueID是一個(gè)由IE所生成的唯一的(在一個(gè)頁(yè)面內(nèi))字符串-它用來識(shí)別HTC的實(shí)例。

  八、 再封裝

  在我們的CHECKBOX中有一個(gè)缺點(diǎn)。按照我們建立它的方式,在constructor()期間被注入的HTML將隸屬于宿主該HTC的頁(yè)面的DOM。而且,全局的JavaScript變量like_value屬于它們所在的文檔的全局范圍。這是危險(xiǎn)的,因?yàn)槲覀兣既粫?huì)遇到命名沖突的可能性:最明顯的情形是使用同一個(gè)組件的多個(gè)實(shí)例。另外這還會(huì)導(dǎo)致一個(gè)可能性-我們的控件可能會(huì)偶然地用相同的名稱參考其它對(duì)象,反之也如此。

  為簡(jiǎn)化起見,需要建立一種專門的機(jī)制來為對(duì)象授權(quán)啟動(dòng)一個(gè)真正模塊化方法。幸好,HTC技術(shù)支持一種智能答案-viewLink。
 
  把一個(gè)控件聲明為封裝的最容易的方法是把一個(gè)額外聲明放到打開和關(guān)閉的PUBLIC:COMPONENT標(biāo)簽之間:

<PUBLIC:DEFAULTS viewLinkContent/>

  該控件立即就變成封裝性的;而且它有自己的HTML文檔樹-成為主文檔的原子組件。該對(duì)象的每個(gè)實(shí)例有它自己的實(shí)例值的集合并且只有公共方法和屬性能夠從外界代碼中加以存取。換句話說,該viewLink機(jī)制充分地啟動(dòng)了復(fù)雜的Web應(yīng)用程序的設(shè)計(jì)和實(shí)現(xiàn)-通過使用一種真正的OO的基于組件的方法。

  特別地,我們可以簡(jiǎn)化代碼-通過從內(nèi)部復(fù)選框和HTML標(biāo)簽的定義中刪除uniqueID后綴,因?yàn)槲覀儾辉贀?dān)心命名沖突。因此,我們可以替換下面這一行:

eval( 'cb_'+uniqueID).checked = ( _value == onValue );

  用

cb.checked = ( _value == onValue );

  并相應(yīng)地改變addCheckbox()和addLabel()。

  九、 結(jié)論

  既然AJAX競(jìng)賽剛剛開始,那么就不存在什么AJAX標(biāo)準(zhǔn)并且沒有現(xiàn)成的你可以依賴以構(gòu)建你的應(yīng)用程序的可廣為接受的RAD工具。雖然軟件供應(yīng)商們可能還需要較長(zhǎng)一段時(shí)間來創(chuàng)建這種強(qiáng)健的開發(fā)平臺(tái),AJAX熱心者已經(jīng)開始著手準(zhǔn)備-通過一些良好定義的API把可重用的代碼塊封裝為商業(yè)組件。

  以這種方向?qū)Ш剑疚母爬薃JAX語(yǔ)言的OO"力量"-JavaScript。另外,還展示了一種可用的組件-授權(quán)策略-客戶端定制標(biāo)簽技術(shù)。我們?cè)趦H描述IE特定的定制標(biāo)簽的同時(shí),還另外提供了一個(gè)可下載的實(shí)例-適于Mozilla瀏覽器的可擴(kuò)展的綁定實(shí)例。

黑咖啡 2006-02-09 13:36 發(fā)表評(píng)論
]]>
用AJAX開發(fā)智能Web應(yīng)用程序之基礎(chǔ)篇 http://www.tkk7.com/faithwind/articles/30021.html黑咖啡黑咖啡Thu, 09 Feb 2006 05:34:00 GMThttp://www.tkk7.com/faithwind/articles/30021.htmlhttp://www.tkk7.com/faithwind/comments/30021.htmlhttp://www.tkk7.com/faithwind/articles/30021.html#Feedback0http://www.tkk7.com/faithwind/comments/commentRss/30021.htmlhttp://www.tkk7.com/faithwind/services/trackbacks/30021.html下載本文源代碼

  一. 什么是AJAX?

  這個(gè)名字代表了異步JavaScript+XMLHTTPRequest,并且意味著你可以在基于瀏覽器的JavaScript和服務(wù)器之間建立套接字通訊。其實(shí)AJAX并不是一種新技術(shù),而是已經(jīng)成功地用于現(xiàn)代瀏覽器中的若干成功技術(shù)的可能性組合。所有的AJAX應(yīng)用程序?qū)崿F(xiàn)了一種“豐富的”UI——這是通過JavaScript操作HTML文檔對(duì)象模型并且經(jīng)由XMLHttpRequest實(shí)現(xiàn)的精確定位的數(shù)據(jù)檢索來實(shí)現(xiàn)的。典型的示例AJAX應(yīng)用程序是Google Labs(http://labs.google.com)的Google Maps和Google Suggest。這些應(yīng)用程序現(xiàn)場(chǎng)監(jiān)視用戶輸入并且提供實(shí)時(shí)的頁(yè)面更新。最重要的是,在用戶通過地圖導(dǎo)航或輸入一個(gè)查找字符串的同時(shí),這些事件不需要刷新頁(yè)面。

  事實(shí)上,支持這些令人感到驚訝的應(yīng)用的技術(shù)已經(jīng)出現(xiàn)一段時(shí)間了,盡管它們要求復(fù)雜的技能以及使用瀏覽器的技巧。一些專利產(chǎn)品就提供了相似的能力——如Macromedia Flash插件,Java Applets或.NET運(yùn)行時(shí)——在達(dá)到實(shí)用上已經(jīng)有一段時(shí)間了。把一種可與服務(wù)器通話的腳本組件引入到瀏覽器中的思想早在IE 5.0中就已經(jīng)存在。Firefox和其它流行的瀏覽器也加入到瀏覽器大軍中并以一種內(nèi)置對(duì)象形式支持XMLHTTPRequest。隨著跨平臺(tái)瀏覽器的出現(xiàn),這些技術(shù)得到了認(rèn)可并在2004年3月一家稱為Adaptive Path的公司中正式提出了AJAX。

  簡(jiǎn)而言之,由于來自于Google的支持和安裝了一點(diǎn)可用的瀏覽器技術(shù),加上為了一種"更好的用戶體驗(yàn)",每個(gè)人都在把客戶端技術(shù)添加到Web應(yīng)用程序上。

  二. AJAX與傳統(tǒng)應(yīng)用程序的區(qū)別

  一個(gè)傳統(tǒng)Web應(yīng)用程序模型實(shí)際上是一種基本的事件——用戶被迫提交表單以實(shí)現(xiàn)頁(yè)面交換。也就是說,表單提交和頁(yè)面?zhèn)魉蜔o法得到保證:還有更壞的情形——用戶需要再次點(diǎn)擊。這與AJAX截然不同-——數(shù)據(jù)跨過線路而不是完整的HTML頁(yè)面?zhèn)鬏敗_@種數(shù)據(jù)交換是經(jīng)由特定的瀏覽器對(duì)象:XMLHttpRequest實(shí)現(xiàn)的;再由適當(dāng)?shù)倪壿媮硖幚砻總€(gè)數(shù)據(jù)請(qǐng)求的結(jié)果,頁(yè)面的特定區(qū)域而不是完整的頁(yè)面被更新。結(jié)果是更快的速度,更少的擁擠和更好的信息傳送控制。

  傳統(tǒng)型"click-refresh"Web應(yīng)用程序強(qiáng)迫用戶中斷工作過程而等待頁(yè)面的重裝。通過引入AJAX技術(shù),一個(gè)客戶端腳本能夠異步地與服務(wù)器通話,而用戶仍能保持輸入數(shù)據(jù)。除了對(duì)用戶透明之外,這樣的異步意味著服務(wù)器可以有更多時(shí)間來處理請(qǐng)求。

  傳統(tǒng)Web應(yīng)用程序把所有的處理代理到服務(wù)器并且強(qiáng)迫服務(wù)器進(jìn)行狀態(tài)管理。AJAX允許靈活劃分應(yīng)用程序邏輯以及客戶和服務(wù)器之間的狀態(tài)管理。這就消除了一種"click-refresh"依賴性并且提供更好的服務(wù)器可伸縮性。當(dāng)該狀態(tài)存儲(chǔ)在客戶端,你就不必跨越服務(wù)器來維持會(huì)話或保存/結(jié)束狀態(tài)-其使用期限是由客戶端來定義的。

  三. AJAX——分布式的MVC

  盡管AJAX應(yīng)用程序依靠JavaScript來實(shí)現(xiàn)描述層,然而處理能力和知識(shí)庫(kù)仍然存在于服務(wù)器上。此時(shí),AJAX應(yīng)用程序大量的與J2EE服務(wù)器通訊——把數(shù)據(jù)輸入/輸出Web服務(wù)和servlets。具有基于AJAX的描述層的J2EE應(yīng)用程序和標(biāo)準(zhǔn)J2EE應(yīng)用程序之間的區(qū)別首先在于,MVC是通過線路分布的。通過使用AJAX,視圖是本地的,而模型和控制器是分布式的——這使得開發(fā)者能夠靈活地決定哪些部件會(huì)是基于客戶端的。具體地說,本地視圖通過巧妙地操作HTML DOM而生成圖形;控制器局部地處理用戶輸入并且根據(jù)開發(fā)者的判斷擴(kuò)展到服務(wù)器的處理——經(jīng)由HTTP請(qǐng)求(Web服務(wù),XML/RPC或其它)實(shí)現(xiàn);模型的遠(yuǎn)程部分是根據(jù)客戶端需要而下載的以達(dá)到實(shí)時(shí)更新客戶端頁(yè)面;并且狀態(tài)是在客戶端收集的。

  在以后的AJAX文章中,我們將比較深入地討論這里的每一種組件并提供有關(guān)它們聯(lián)合在一起進(jìn)行應(yīng)用的示例。現(xiàn)在,先不多說,讓我們?cè)敿?xì)地分析一個(gè)簡(jiǎn)單的AJAX示例。

  四. 郵政區(qū)號(hào)校驗(yàn)和查詢

  我們將創(chuàng)建一個(gè)包含三個(gè)INPUT字段(Zip,City和State)的HTML頁(yè)面。我們將保證,只要用戶輸入郵政區(qū)號(hào)的前三個(gè)數(shù)字,該頁(yè)面上的字段就會(huì)用第一個(gè)匹配的狀態(tài)值填充。一旦用戶輸入了所有五位郵政區(qū)號(hào)數(shù),我們將立即決定和填充相應(yīng)的城市。如果郵政區(qū)號(hào)無效(在服務(wù)器的數(shù)據(jù)庫(kù)沒有找到),那么我們將把郵政區(qū)號(hào)的邊界設(shè)置為紅色。這樣的可視化線索有助于用戶并且在現(xiàn)代瀏覽器中已經(jīng)成為一種標(biāo)準(zhǔn)(作為一實(shí)例,當(dāng)Firefox找到一個(gè)HTML頁(yè)面中的匹配關(guān)鍵字時(shí),它會(huì)高亮與你在瀏覽器查找域輸入的內(nèi)容一致的部分)。

  讓我們首先創(chuàng)建一個(gè)簡(jiǎn)單的包含三個(gè)INPUT字段的HTML:zip,city和state。請(qǐng)注意,一旦一個(gè)字符輸入進(jìn)郵政區(qū)號(hào)字段域中,即調(diào)用方法zipChanged()。JavaScript函數(shù)zipChanged()(見下)在當(dāng)zip長(zhǎng)度為3時(shí)調(diào)用函數(shù)updateState(),而在當(dāng)zip長(zhǎng)度為5時(shí)調(diào)用函數(shù)up-dateCity()。而updateCity()和updateState()把大部分的工作代理到另一個(gè)函數(shù)ask()。

Zip:<input id="zipcode" type="text" maxlength="5" onKeyUp="zipChanged()"
style="width:60"/>
City: <input id="city" disabled maxlength="32" style="width:160"/>
State:<input id="state" disabled maxlength="2" style="width:30"/>
<script src="xmlhttp.js"></script>
<script>
var zipField = null;
function zipChanged(){
zipField = document.getElementById("zipcode")
var zip = zipField.value;
zip.length == 3?updateState(zip):zip.length == 5?updateCity(zip):"";
}
function updateState(zip) {
 var stateField = document.getElementById("state");
 ask("resolveZip.jsp?lookupType=state&zip="+zip, stateField, zipField);
}
function updateCity(zip) {
 var cityField = document.getElementById("city");
 ask("resolveZip.jsp? lookupType=city&zip="+zip, cityField, zipField);
}
</script>

  函數(shù)ask()與服務(wù)器進(jìn)行通訊并分配一個(gè)回調(diào)函數(shù)來處理服務(wù)器的響應(yīng)(見下列代碼)。后面,我們將分析具有雙重特點(diǎn)的resolveZip.jsp的內(nèi)容-它根據(jù)zip字段中的字符數(shù)查找city或state信息。重要的是,ask()使用了具有異步特點(diǎn)的XmlHttpRequest,這樣填充state和city字段或著色zip字段邊界就可以不必減慢數(shù)據(jù)入口而得以實(shí)現(xiàn)。首先,我們調(diào)用request.open()-它用服務(wù)器打開套接字頻道,使用一個(gè)HTTP動(dòng)詞(GET或POST)作為第一個(gè)參數(shù)并且以數(shù)據(jù)提供者的URL作為第二個(gè)參數(shù)。request.open()的最后一個(gè)參數(shù)被設(shè)置為true-它指示該請(qǐng)求的異步特性。注意,該請(qǐng)求還沒有被提交。隨著對(duì)request.send()的調(diào)用,開始提交-這可以為POST提供任何必要的有效載荷。在使用異步請(qǐng)求時(shí),我們必須使用request.onreadystatechanged屬性來分配請(qǐng)求的回調(diào)函數(shù)。(如果請(qǐng)求是同步的話,我們應(yīng)該能夠在調(diào)用request.send之后立即處理結(jié)果,但是我們也有可能阻斷用戶,直到該請(qǐng)求完成為止。)

HTTPRequest = function () {
 var xmlhttp=null;
 try {
  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
 } catch (_e) {
  try {
   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (_E) { }
 }
 if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
  try {
   xmlhttp = new XMLHttpRequest();
  } catch (e) {
   xmlhttp = false;
  }
 }
 return xmlhttp;
}

function ask(url, fieldToFill, lookupField) {
 var http = new HTTPRequest();
 http.open("GET", url, true);
 http.onreadystatechange = function (){ handleHttpResponse(http, fieldToFill,lookupField)};
 http.send(null);
}

function handleHttpResponse(http, fieldToFill, lookupField) {
 if (http.readyState == 4) {
  result = http.responseText;
  if ( -1 != result.search("null") ) {
   lookupField.style.borderColor = "red";
   fieldToFill.value = "";
  } else {
   lookupField.style.borderColor = "";
   fieldToFill.value = result;
  }
 }
}

  為ask()所使用的HttpRequest()函數(shù)(見上)是一跨瀏覽器的XMLHTTPRequest的一個(gè)實(shí)例的構(gòu)造器;稍后我們將分析它。到目前為止,請(qǐng)注意對(duì)于handleResponse()的調(diào)用是如何用一匿名函數(shù)包裝的-這個(gè)函數(shù)是function(){handleHttpResponse(http,fieldToFill, lookupField)}。

  該函數(shù)的代碼是動(dòng)態(tài)創(chuàng)建的并且在每次我們給http.onreadstatechange屬性賦值時(shí)被編譯。結(jié)果,JavaScript創(chuàng)建一個(gè)指向上下文(所有的變量都可以存取正在結(jié)束的方法-ask())的指針。這樣以來,匿名函數(shù)和handleResponse()就能夠被保證充分存取所有的上下文宿主的變量,直至到匿名函數(shù)的參考被垃圾回收站收集為止。換句話說,無論何時(shí)我們的匿名函數(shù)被調(diào)用,它都能無縫地參考request,fieldToFill和lookupField變量,就象它們是全局的一樣。而且,每次ask()調(diào)用都將創(chuàng)建環(huán)境的一個(gè)獨(dú)立拷貝,并且此時(shí)這些變量中保存有該函數(shù)將結(jié)束時(shí)的值。

  現(xiàn)在,讓我們分析一下函數(shù)handleResponse()。既然它能夠在請(qǐng)求處理的不同狀態(tài)下激活,那么該函數(shù)將忽略所有的情形-除了該請(qǐng)求處理完成之外-這相應(yīng)于request.readyState屬性等于4("Completed")。此時(shí),該函數(shù)讀取服務(wù)器的響應(yīng)文本。與它的名字所暗示的相反,XmlHttpRequest的輸入和輸出都不必限于XML格式。特別地,我們的resolveZip.jsp(見源碼中的列表1)返回普通文本。如果返回值為"unknown",那么該函數(shù)將假定郵政區(qū)號(hào)是無效的并且把查找字段(zip)邊界顏色置為紅色。否則,返回值被用于填充字段state或city,并且zip的邊界被賦予一種缺省顏色。

  XMLHttpRequest-傳輸對(duì)象

  讓我們返回到我們的XMLHTTPRequest的跨瀏覽器實(shí)現(xiàn)。最后一個(gè)列表包含一個(gè)HttpRequest()函數(shù)-它向上兼容于IE5.0和Mozilla 1.8/FireFox。為簡(jiǎn)化起見,我們只創(chuàng)建一個(gè)微軟XMLHTTPRequest對(duì)象,而且如果創(chuàng)建失敗,我們假定它是Firefox/Mozilla。

  該函數(shù)的核心是XMLHTTPRequest-這是一個(gè)本機(jī)瀏覽器對(duì)象,它為包括HTTP協(xié)議的任何東西與服務(wù)器之間的通訊提供方便。它允許指定任何HTTP動(dòng)詞,頭部和有效載荷,并且能夠以異步或同步方式工作。不需要下載也不需要安裝任何插件-盡管在IE的情形下,XMLHTTPRequest是一個(gè)集成到瀏覽器內(nèi)部的ActiveX。因而,"Run ActiveX Control and Plugins"默認(rèn)IE權(quán)限應(yīng)該正好適合使用它。

  最重要的是,XMLHTTPRequest允許一個(gè)到服務(wù)器的RPC風(fēng)格的編程查詢而不需要任何頁(yè)面刷新。它以一種可預(yù)測(cè)的,可控制的方式來實(shí)現(xiàn)此-提供了到HTTP協(xié)議的所有細(xì)節(jié)的完整存取-包括頭部和數(shù)據(jù)的任何定制格式。在以后的文章中,我們將向你展示其它一些業(yè)界協(xié)議-你可以在這些傳輸協(xié)議(如Web服務(wù)和XML-RPC)之上運(yùn)行-它們極大地簡(jiǎn)化大規(guī)模應(yīng)用程序的開發(fā)和維護(hù)。
五.服務(wù)器端邏輯

  最后,服務(wù)器端的resolveZip.jsp被從函數(shù)ask()中調(diào)用(見所附源碼中的列表1)。這個(gè)resolveZip.jsp在兩種由當(dāng)前的郵政區(qū)號(hào)長(zhǎng)度所區(qū)分的獨(dú)立的場(chǎng)所下被調(diào)用(見zipChanged()函數(shù))。請(qǐng)求參數(shù)lookupType的值或者是state或者是city。為簡(jiǎn)化起見,我們將假定,兩個(gè)文件state.properties和city.properties都位于服務(wù)器中C驅(qū)動(dòng)器的根目錄下。resolveZip.jsp邏輯負(fù)責(zé)用適當(dāng)?shù)念A(yù)裝載的文件返回查找值。
我們的支持AJAX的頁(yè)面現(xiàn)在已經(jīng)準(zhǔn)備好了。

  六.遠(yuǎn)程腳本技術(shù)-一種可選方法

  一些更舊的AJAX實(shí)現(xiàn)是基于所謂的遠(yuǎn)程腳本技術(shù)。這種思想是,用戶的行為導(dǎo)致經(jīng)由IFRAME對(duì)服務(wù)器進(jìn)行查詢,而服務(wù)器用JavaScript作出響應(yīng),該腳本一旦到達(dá)客戶端立即被執(zhí)行。這與XMLHttpRequest方法相比存在較大的區(qū)別,在后者情況下,服務(wù)器響應(yīng)數(shù)據(jù)而客戶端解釋數(shù)據(jù)。其好處是這種解決方案支持更舊的瀏覽器。

  基于IFRAME示例的HTML部分(見所附源碼中的列表2)與我們?cè)赬MLHTTPRequest場(chǎng)合下所用的極相似,但是這次我們將引入另外一個(gè)IFRAME元素-controller:

Zip:<input id="zipcode" type="text" maxlength="5" onKeyUp="zipChanged()"
style="width:60" size="20"/>
City: <input id="city" disabled maxlength="32" style="width:160" size="20"/>
State:<input id="state" disabled maxlength="2" style="width:30" size="20"/>
<iframe id="controller" style="visibility:hidden;width:0;height:0"></iframe>

  我們保持每次擊鍵都調(diào)用zipChanged()一次,但是這一次,從zipChanged()中被調(diào)用的函數(shù)ask()(見所附源碼中的列表3)負(fù)責(zé)設(shè)置IFRAME的src屬性,而不是調(diào)用一個(gè)XMLHTTPRequest:

function ask(url, fieldToFill, lookupField){
 var controller = document.getElementById("controller");
 controller.src= url+"&field="+fieldToFill.id+"&zip="+lookupField.id;
}

  服務(wù)器端邏輯由一個(gè)粗略的resolveZip.jsp(見所附源碼中的列表4)所描述。它與它的XMLHTTPRequest對(duì)應(yīng)物相區(qū)別-它返回JavaScript語(yǔ)句,這些語(yǔ)句設(shè)置變量字段lookup和city的全局值,而且一旦它到達(dá)瀏覽器即從全局窗口的執(zhí)行上下文中調(diào)用函數(shù)response()。

  函數(shù)response()是一修改版本的handleResponse()-這一函數(shù)可以免于處理未完成的請(qǐng)求(詳見本文所附源碼中的列表2)。

  七. 難題

  為簡(jiǎn)化起見,讓我們"俯看"一下在我們的示例代碼中的一些重要的問題:

  1.事實(shí)-XMLHTTPRequest對(duì)象實(shí)例和回調(diào)函數(shù)調(diào)用在被使用以后并沒被破壞-在每次調(diào)用后這有可能導(dǎo)致內(nèi)存泄漏。適當(dāng)編寫的代碼應(yīng)該破壞或重用對(duì)象池中的這些實(shí)例。而且,客戶端必須使用與服務(wù)器軟件相同的對(duì)象管理技術(shù)。

  2.在大多數(shù)情況下,錯(cuò)誤往往得不到有效處理。例如,在方法ask()中對(duì)request.open()的調(diào)用可能引發(fā)一個(gè)異常,這是必須要捕獲和處理的,即使在瀏覽器中沒有設(shè)置JavaScript異常自動(dòng)捕獲功能。而handleResponse()函數(shù)又是另外一個(gè)例子。它必須要為可能的服務(wù)器端和通訊錯(cuò)誤而檢查headers和responseText值。在發(fā)生錯(cuò)誤的情況下,它必須盡力恢復(fù)并/或者報(bào)告錯(cuò)誤。正確開發(fā)的AJAX應(yīng)用程序要盡可能避免"提交"松散的數(shù)據(jù),因?yàn)橥嬖诰€路斷開和其它低級(jí)通訊的問題-所以這些程序必須建立一個(gè)強(qiáng)壯的和自恢復(fù)的框架為此提供支持。

  3.當(dāng)前服務(wù)器端框架提供相當(dāng)多的功能-它們可以與一種自由刷新方法和諧相處。例如,讓我們考慮一個(gè)定制的在指定時(shí)間內(nèi)的服務(wù)器端認(rèn)證的問題。在這種情況下,我們必須攔截到XMLHTTPRequest調(diào)用的安全系統(tǒng)響應(yīng),顯示登錄屏幕,然后在用戶被認(rèn)證后重新發(fā)出請(qǐng)求。

  所有的這些問題只是一些典型的用低級(jí)API工作的任何應(yīng)用程序代碼,而且所有這些問題都能被解決。好消息是,解決這些問題所需要的技術(shù)十分相似于大多數(shù)Java開發(fā)技術(shù),如Web服務(wù),定制標(biāo)簽和XML/XSLT。唯一的區(qū)別在于,現(xiàn)在這些技術(shù)以下列形式用于客戶端:

  ·Web服務(wù)-使用SOAP/REST/RPC等簡(jiǎn)單通訊標(biāo)準(zhǔn)

  ·客戶端定制標(biāo)簽-打包豐富的客戶端控件并集成AJAX功能

  ·數(shù)據(jù)操作-基于XML和基于XSLT技術(shù)

  八. 小結(jié)

  AJAX方法能夠向人們提供一種與桌面應(yīng)用程序相同的豐富的互聯(lián)網(wǎng)體驗(yàn)。但是,我們必須有選擇地使用AJAX技術(shù),如當(dāng)你仍在線購(gòu)物時(shí),你絕對(duì)不想讓你的信用卡通過后臺(tái)處理就悄悄地開始付款。AJAX會(huì)成為一種持續(xù)的動(dòng)力嗎?我們當(dāng)然希望這樣。在過去的五年時(shí)間內(nèi)我們一直在努力開發(fā)AJAX應(yīng)用程序并且能證明它是健全并且很有效的。然而,它要求一個(gè)開發(fā)者必須精通大量技術(shù)而不是在傳統(tǒng)的"click-refresh"Web應(yīng)用程序中所使用的那些。

黑咖啡 2006-02-09 13:34 發(fā)表評(píng)論
]]>
Struts+Hibernate簡(jiǎn)化J2EE的文件操作http://www.tkk7.com/faithwind/articles/30020.html黑咖啡黑咖啡Thu, 09 Feb 2006 05:32:00 GMThttp://www.tkk7.com/faithwind/articles/30020.htmlhttp://www.tkk7.com/faithwind/comments/30020.htmlhttp://www.tkk7.com/faithwind/articles/30020.html#Feedback0http://www.tkk7.com/faithwind/comments/commentRss/30020.htmlhttp://www.tkk7.com/faithwind/services/trackbacks/30020.html1. 引言

  每位Web開發(fā)者在工程中都必須實(shí)現(xiàn)至少實(shí)現(xiàn)一個(gè)客戶文件的上載功能。永遠(yuǎn)需要!然而,要求用戶僅提供一個(gè)指向其數(shù)據(jù)的URL是不公平的。作為一個(gè)開發(fā)者,幫助用戶順利地完成這些正是你的工作。HTTP協(xié)議的使用將十分有助于解決這個(gè)問題,但是許多開發(fā)者并沒有選擇使用HTTP協(xié)議。

  你需要解決的問題有:存儲(chǔ)上載的文件并找到關(guān)于問題"Where?","Why?"和"How?"等等的有關(guān)答案。

  本文將解釋所有在解決這些問題中遇到的瓶頸,并提供了功能性的、易于理解的代碼,這些內(nèi)容很可能會(huì)應(yīng)用于你將來的工程中。

  2. 準(zhǔn)備工作

  本文將使用當(dāng)前最流行的開發(fā)工具,它們是:

  ·應(yīng)用程序服務(wù)器:WebLogic 8.1 SP3服務(wù)器

  ·基于Java的構(gòu)建工具:Apache Ant 1.6.2

  ·數(shù)據(jù)庫(kù)服務(wù)器:MySQL 4.0.16

  ·用于從Java連接到MySQL的:MySQL Connector/J 3.1.7

  Struts 1.2.4用作構(gòu)建Java Web應(yīng)用程序的框架,而Hibernate 3.0(RC1)用于對(duì)象/關(guān)系持續(xù)性操作和查詢服務(wù)。

  本文雖然基于Windows平臺(tái)寫成,但在其它操作系統(tǒng)之上,應(yīng)該稍作修改就能運(yùn)行。

  另外,讀者還應(yīng)熟悉BEA WebLogic服務(wù)器以及使用Struts和Hibernate進(jìn)行J2EE應(yīng)用程序的開發(fā)。篇幅所限,本文并沒有討論關(guān)于應(yīng)用程序和數(shù)據(jù)庫(kù)服務(wù)器配置的問題。

  3. 上載工程分析

  現(xiàn)在讓我們討論存儲(chǔ)上載文件的機(jī)制,并回答上面列出的三個(gè)問題。

  ·Where?你將會(huì)把上載文件存儲(chǔ)到一個(gè)數(shù)據(jù)庫(kù)中。

  ·Why?在許多情況下,它確實(shí)是合適的解決方案。使用本文的解決方案,你不會(huì)因同步上載文件而煩惱,一旦你正在備份著應(yīng)用程序-你只需要備份數(shù)據(jù)庫(kù)就可以了。而且,你不需要與一個(gè)用戶及其在一文件系統(tǒng)上的文件一直保持十分笨拙的聯(lián)系。

  ·How?可以使用BLOB(二進(jìn)制大型對(duì)象)字段實(shí)現(xiàn)。這樣的字段用于存儲(chǔ)大型的并且經(jīng)常是原始或二進(jìn)制的格式。Hibernate可以使你非常容易地操作這些字段。

  典型情況下,一個(gè)企業(yè)應(yīng)用程序(EAR)由兩部分組成:Web層(WAR)和商業(yè)層(EJB)。商業(yè)層包含一個(gè)無狀態(tài)的會(huì)話bean-它借助于Hibernate的幫助實(shí)現(xiàn)數(shù)據(jù)的存儲(chǔ)。圖1顯示了EJB的遠(yuǎn)程接口。


圖1.HelloSession EJB的接口。

  從Web層角度看,這個(gè)EJB為商業(yè)代理所存取。

  注意,該代碼使用了一個(gè)類User的對(duì)象。User代表什么意思?它是一個(gè)保留在數(shù)據(jù)庫(kù)中的用戶實(shí)體的"Plain Old Java Object"(POJO)。你將會(huì)活躍地使用這個(gè)UserPOJO。設(shè)置它的屬性并請(qǐng)求EJB來存儲(chǔ)它,然后帶回一個(gè)所有的已存在于數(shù)據(jù)庫(kù)中的User實(shí)體的列表。或者,由它取回一個(gè)專門的User實(shí)體并存入POJO中,然后使用一個(gè)getter存取器來存取它的屬性(見圖2)。


圖2.所有的Web層的servlet都使用該User POJO。

  非常明顯,Web層僅由三個(gè)servlet(Struts Action的)組成,一個(gè)用于上載文件,一個(gè)用于下載文件,一個(gè)用于列出所有的User實(shí)體及其相關(guān)文件。

  ·DownloadFileAction:該servlet僅使用一個(gè)參數(shù)id,這是在數(shù)據(jù)庫(kù)中的一個(gè)用戶的id。然后,它裝入該用戶的實(shí)體并把該文件從BLOB字段傳遞到ServletOutputStream。

  ·UploadFileAction:該servlet負(fù)責(zé)從一個(gè)HTML表單中提取數(shù)據(jù)并用這些數(shù)據(jù)進(jìn)一步生成DynaActionForm。它僅提取用戶名和上載的文件。

  ·ListAllFilesAction:該servlet沒有輸入?yún)?shù)或數(shù)據(jù),僅負(fù)責(zé)從數(shù)據(jù)庫(kù)中裝入所有可用的用戶User實(shí)體。
4. 環(huán)境準(zhǔn)備

  如果所有相應(yīng)的軟件被正確下載并安裝在你的PC上,那么下一步,你就可以準(zhǔn)備數(shù)據(jù)庫(kù)和存取該數(shù)據(jù)庫(kù)的用戶而且還要使用名為MySqlDS的JNDI設(shè)置好連接池與數(shù)據(jù)源。同時(shí),請(qǐng)肯定MySQL Connector/J存在于你的類路徑中!要檢查這一點(diǎn),只需輸入:

echo %CLASSPATH%

  文件mysql-connector-java-3.1.7-bin.jar(帶有完整的路徑)應(yīng)該于此。這是必需的,因?yàn)閃ebLogic需要查找到MySQL Connector/J并用該驅(qū)動(dòng)程序進(jìn)行工作。

  現(xiàn)在,既然一切準(zhǔn)備妥當(dāng),我們就可以在數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)表以用于保存用戶實(shí)體。以任何MySQL客戶身份登錄到該數(shù)據(jù)庫(kù),然后輸入:

drop table if exists TABLE_USER_FILE;
create table TABLE_USER_FILE
(
USER_ID int auto_increment not null,
USER_USERNAME text,
USER_FILENAME text,
USER_FILETYPE text,
USER_FILESIZE text,
USER_FILEBIN longblob,
primary key (USER_ID)
)
type = MyISAM;

  你看,一切都很簡(jiǎn)單。也許在此你最陌生的是longblob類型。二進(jìn)制大型對(duì)象(BLOB)列是MySQL的秘密武器之一。這些列中存儲(chǔ)了二進(jìn)制的數(shù)據(jù),你可以象其它普通的數(shù)據(jù)類型一樣來檢索和操縱它。根據(jù)MySQL指南有關(guān)資料,BLOB是一個(gè)二進(jìn)制大型對(duì)象,它能容納不同大小的數(shù)據(jù)。事實(shí)上,MySQL有四種BLOB類型:

  ·tinyblob:僅255個(gè)字符

  ·blob:最大限制到65K字節(jié)

  ·mediumblob:限制到16M字節(jié)

  ·longblob:可達(dá)4GB

  在每個(gè)MySQL的文檔(從MySQL4.0開始)的介紹中,一個(gè)longblob列的最大允許長(zhǎng)度依賴于在客戶/服務(wù)器協(xié)議中可配置的最大包的大小和可用內(nèi)存數(shù)。

  你可能對(duì)在BLOB中存儲(chǔ)大型文件非常謹(jǐn)慎,但是請(qǐng)放心使用,MYSQL提供了這樣的靈活性!最大包的大小可容易地通過文件my.ini中的適當(dāng)行進(jìn)行設(shè)置。例如:

set-variable = max_allowed_packet=10M

  你能指定幾乎任何你需要的大小。默認(rèn)是1M。

  現(xiàn)在,在Hibernate和WebLogic之間還存在一個(gè)問題。根據(jù)Hibernate的文檔,Hibernate3.0使用ANTLR作為它的新的查詢分析器。這真是個(gè)遺憾!但是BEA Weblogic在系統(tǒng)類路徑中包括了ANTLR的一個(gè)版本,它在任何應(yīng)用程序庫(kù)裝入前就已經(jīng)被加載了。因?yàn)閃eblog似乎不支持屬性類裝載器隔離,在應(yīng)用程序的上下文中它是不會(huì)看到該Hibernate類的。BEA好象在包名前加上前綴來解決這個(gè)問題,但是現(xiàn)已發(fā)布的ANTLR并沒有這個(gè)前綴。

  這個(gè)問題的解決辦法是,把所有的Hibernate和依賴庫(kù)放到你的CLASSPATH中。就象如下這樣:

C:\green\te3>echo %CLASSPATH%
.;C:\mysql-connector-java-3.1.7\mysql-connector-java-3.1.7-bin.jar;C:\hibernate-3.0\hibernate3.jar;
C:\hibernate-3.0\lib\ehcache-1.1.jar;C:\hibernate-3.0\lib\jta.jar;C:\hibernate-3.0\lib\xml-apis.jar;
C:\hibernate-3.0\lib\commons-logging-1.0.4.jar;C:\hibernate-3.0\lib\dom4j-1.5.2.jar;
C:\hibernate-3.0\lib\antlr-2.7.4.jar;C:\hibernate-3.0\lib\cglib-full-2.0.2.jar;
C:\hibernate-3.0\lib\jdbc2_0-stdext.jar;C:\hibernate-3.0\lib\xerces-2.6.2.jar;
C:\hibernate-3.0\lib\jaxen-1.1-beta-4.jar;C:\hibernate-3.0\lib\commons-collections-2.1.1.jar;
C:\hibernate-3.0\lib\log4j-1.2.9.jar;

  現(xiàn)在,unzip源代碼到任何你想要的目錄下。用你喜歡的文件編輯器打開build.xml文件,并檢查(如果必要的話,加以改變)前面涉及你的HOME目錄的幾行和你的域的標(biāo)題。保存你的變化并輸入:

  ant

  當(dāng)工程建構(gòu)完成時(shí),你就會(huì)得到一個(gè)文件TE3.EAR,這是一個(gè)準(zhǔn)備好等待發(fā)布的包(名字TE3僅是個(gè)普通名字)。然后,你就可以用WebLogic的管理控制臺(tái)發(fā)布它,當(dāng)發(fā)布后,用你的瀏覽器http://localhost:7001/te3/打開它。之后,你將看到兩個(gè)選項(xiàng):"upload file"和"list all files"。
5. 代碼分析

  現(xiàn)在,你已經(jīng)看到了一切是如何工作的,下面解釋一下幾個(gè)更為重要的代碼片斷。如前述,UploadFileAction.java使用DynaActionForm來保持HTML表單的屬性。下面是它在/WEB-INF/struts-config.xml文件中的定義:

...
<form-beans>
 <form-bean name="uploadFileForm" type="org.apache.struts.action.DynaActionForm" dynamic="true">
  <form-property name="myFile" type="org.apache.struts.upload.FormFile"/>
  <form-property name="myName" type="java.lang.String"/>
 </form-bean>
</form-beans>
...
<action-mappings>
<action path="/UploadFile" type="com.prokhorenko.web.UploadFileAction" name="uploadFileForm">
...

  下面的代碼教你怎樣通過屬性存取來"存儲(chǔ)"一個(gè)上傳的文件:

...
User per = new User();
DynaActionForm df = (DynaActionForm) form;
FormFile myFile = (FormFile) df.get("myFile");
...
per.setFilebin ( Hibernate.createBlob (myFile.getInputStream()) );
...

  Hibernate.createBlob(...)返回初始的不變的java.sql.Blob對(duì)象并使用它,因?yàn)闉榱嗽O(shè)置User實(shí)體的filebin屬性,該屬性被定義并被映射為java.sql.Blob。

  接下去一段有趣的代碼來自于DownloadFileAction.java,它用’id’加載該User實(shí)體:

...
User per = bd.getUser( new Long((String)request.getParameter("id")) );
...

  下一步,你就需要設(shè)置響應(yīng)的頭部,并開始把Blob型字段filebin的內(nèi)容寫到ServletOutputStream中:

...
ServletOutputStream outStream = response.getOutputStream();
InputStream in = per.getFilebin().getBinaryStream();
byte[] buffer = new byte[32768];
int n = 0;
while ( ( n = in.read(buffer)) != -1) {
 outStream.write(buffer, 0, n);
}
in.close();
outStream.flush();
...

  6. 最容易的解決方案

  在所有官方檔案中,Hibernate 3.0都包裝了Blob和Clob的實(shí)例,這樣以來就允許具有類型Blob或Clob的屬性的類可以被分離、串行化、反串行化以及被傳遞而實(shí)現(xiàn)合并的目的。因此,你會(huì)看到,Struts和Hibernate幾乎為你做了一切事情-而需要你做的僅僅是極少的幾個(gè)步驟。

  現(xiàn)在看來,上載文件并把它們存儲(chǔ)到數(shù)據(jù)庫(kù)中已不再是象以前那樣是一項(xiàng)繁重的任務(wù)。你只需選擇正確的工具并知道如何靈活地使用它們即可!

黑咖啡 2006-02-09 13:32 發(fā)表評(píng)論
]]>
主站蜘蛛池模板: 日韩精品久久久久久免费 | 国产三级免费观看| 免费在线观看黄网| 国产亚洲美女精品久久久| 亚洲精品国产精品乱码不99| 久久久久亚洲av无码专区喷水| 亚洲av乱码一区二区三区| 国产亚洲男人的天堂在线观看 | 香港特级三A毛片免费观看| 国产久爱免费精品视频| 久久免费视频99| 成人影片麻豆国产影片免费观看 | 亚洲日本一区二区一本一道| 亚洲av不卡一区二区三区| 亚洲国产精品综合久久20| 无人视频免费观看免费视频| 免费视频一区二区| 成年女人色毛片免费看| 亚洲日韩在线观看| 亚洲成综合人影院在院播放| 处破女第一次亚洲18分钟| 免费人成网站在线观看不卡| 麻豆国产VA免费精品高清在线| 亚洲毛片αv无线播放一区| 亚洲w码欧洲s码免费| 成人免费网站视频www| 88av免费观看入口在线| 成人亚洲综合天堂| 亚洲精品熟女国产| 狼色精品人妻在线视频免费| 污污网站18禁在线永久免费观看| 热99re久久免费视精品频软件| 亚洲深深色噜噜狠狠爱网站| 日韩亚洲人成在线| 中国videos性高清免费| 四虎成人精品一区二区免费网站| 久久久久亚洲精品影视| 免费人人潮人人爽一区二区 | 每天更新的免费av片在线观看| 亚洲av成人一区二区三区在线观看| 337p日本欧洲亚洲大胆色噜噜|