概述
Java Server Pages (JSP) 標(biāo)準(zhǔn)標(biāo)記庫 (JSTL)的版本的發(fā)布對于JSP/servlet開發(fā)者來說是一個重大的進(jìn)展。有了一個表達(dá)語言(EL)和一組四種強(qiáng)大的、易于學(xué)習(xí)的標(biāo)準(zhǔn)標(biāo)記庫,JSTL極有可能不久后成為實現(xiàn)動態(tài)的、基于Java的站點的主要方法。
1996年對JSP servlet的介紹使得Java成為動態(tài)網(wǎng)頁開發(fā)的合理的選擇。隨后出現(xiàn)的Java服務(wù)器頁(JSP)是走向快速的、可維護(hù)的Java網(wǎng)頁實現(xiàn)過程的合理進(jìn)化的一步,緊接著就出現(xiàn)了JSP標(biāo)記的支持。但是2002中期的JSTL(JSP標(biāo)準(zhǔn)標(biāo)記庫)的發(fā)布才是進(jìn)一步加速和簡化開發(fā)過程最大的一步。
在本文中,我解釋了JSTL的功能,也涵蓋了你啟動JSTL所需的每件事情。假設(shè)你有了對Java、JSP、XML和安裝一個Web容器的基本理解。如果你對這些主題感到陌生,你可以在Resources(
http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl.html?#resourcesresources)上瀏覽一下背景參考。在下面的闡述中,假定你有XML和SQL的知識。
安裝JSTL支持 對于我們的JSTL安裝例子來說,我們使用Tomcat 4.1(盡管任意支持Servlet 2.3和JSP1.2說明的servlet容器都應(yīng)該能工作)。首先,下載Tomcat 4.1并且按指示進(jìn)行安裝(注意:JSTL需要一個JSP1.2 Web容器)。
用tomcat4 start程序啟動Tomcat,并且運(yùn)行index.html頁來確保Tomcat是激活的、且運(yùn)行良好。
接下來,安裝JSTL支持,你可以從Jakarta站點下載JSTL支持,(
http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl.html?#resourcesresources)并按照下面步驟:
1.從Jakarta站點下載JSTL檢索、解壓/脫檔該文件。(
http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl.html?#resources)
2.復(fù)制你已經(jīng)解壓為common/lib的jar文件到你的Tomcat安裝路徑中(盡管我們的項目不需要所有的jar文件);這使得JSTL文件適用于你的任意Web應(yīng)用。
3.對于任意你想使用JSTL的Web應(yīng)用來說,復(fù)制.tld文件到你的Web應(yīng)用的WEB-INF目錄下。
4.對于你的JSTL的Web應(yīng)用,編輯web.xml并添加下列輸入:
<taglib>
<taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
<taglib-location>/WEB-INF/fmt.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/sql</taglib-uri>
<taglib-location>/WEB-INF/sql.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/x</taglib-uri>
<taglib-location>/WEB-INF/x.tld</taglib-location>
</taglib> |
這些輸入讓你的Web應(yīng)用可使用JSTL標(biāo)記庫的表達(dá)語言(EL)版本。這些輸入的位置有關(guān)系!如果你不肯定將他們輸入到何處,關(guān)于web.xml選項和順序的權(quán)威性指導(dǎo)在文檔類型定義(DTD)中定義過:
http://java.sun.com/j2ee/dtds/web-app_2_2.dtd。
5.當(dāng)你創(chuàng)建了一個使用JSTL的JSP頁時,將他輸入到你的Web應(yīng)用的主目錄下,就像其它JSP和HTML頁一樣。你可以為該頁任意取名,但是它必須有一個.jsp的擴(kuò)展名。
基礎(chǔ)原理 首先,所有的JSTL也是JSP頁,JSTL只是JSP功能的一個超集。
同樣,所有的JSTL標(biāo)記是有效的XML。這意味著:如果你將JSTL標(biāo)記之外的內(nèi)容當(dāng)作臨時文本(通常是HTML)時,剩下的JSTL標(biāo)記必須當(dāng)作合法的XML來解釋。這里有一些隱含的規(guī)則,它要求大部分XML語法都是正確的。
JSTL提供一組四個標(biāo)準(zhǔn)標(biāo)記庫(core核心、internationalization/format國際標(biāo)準(zhǔn)化/格式、XML和SQL)并支持EL。JSTL和EL的基本設(shè)計目的是簡化網(wǎng)頁開發(fā)和實現(xiàn)。
在本文中,我們遵循JSTL說明書的命名規(guī)則,并且把JSTL標(biāo)記當(dāng)作動作。一個JSTL標(biāo)記對應(yīng)于某個動作;調(diào)用動作明確的提醒我們:他們添加動態(tài)行為到一個另外的靜態(tài)頁上。
JSTL標(biāo)記庫有兩個版本:一個用你以前使用的方法將JSP表達(dá)式插入,如<%= . . . %>,另一個使用一個JSTL EL.我會在后面進(jìn)一步討論在JSTL中的EL支持。
EL支持 要理解當(dāng)前JSTL對EL支持的狀態(tài),讓我們檢查相關(guān)的說明書是怎樣處理的。Java說明書請求(JSR)專家組成員決定:由于某種原因,EL規(guī)范說明應(yīng)該是JSP說明書的一部份,而不是JSTL說明書的一部分。EL完整的說明書成為JSP2.0的一部分。因為JSTL1.0在JSP1.3之前已經(jīng)完成,JSTL作者不得不對用于JSTL1.0的EL支持實現(xiàn)做一個合理的猜測(結(jié)果可能是相當(dāng)好的)。一個JSTL維護(hù)版本將與JSP1.3說明書保持一致,并為了使EL適合JSP1.3說明書,JSTL維護(hù)版本做了一些修正。
本文描述的EL概要可能在即將發(fā)布的JSTL版本中有一點改動,但是任何改動都是微小的。
EL用于簡單的語法定義了一套功能強(qiáng)大的語言,它很容易學(xué)習(xí)。它融合著JavaScript語言和Perl語言較好的部分的一些風(fēng)格。EL表達(dá)式,集成了來自四種標(biāo)準(zhǔn)標(biāo)記庫的JSTL標(biāo)記,提供了一個巨大的、靈活的屬性集。
所有的EL表達(dá)式都被${}括起來。JSTL中的表達(dá)式總是JSTL標(biāo)記中的屬性值的一部分。表達(dá)式可以是屬性的一部分或者合并和嵌入到一個字符串直接量中。JSTL屬性也可以包含簡單的字符串直接量。在接下來的JSTL中,我們在一個來自核心庫的c:out動作中說明每種情況,這個核心庫將它的value屬性值送到JSP輸出上:
<c:out value="${anExpression}"/>
<c:out value="literalText${anExpression}${anotherExpression}"/>
<c:out value="literalText"/> |
EL也定義了一套規(guī)則用于強(qiáng)制表達(dá)式中的值轉(zhuǎn)換為上下文相對應(yīng)的類型。我們在這里不詳細(xì)討論這些規(guī)則;然而,它的方法與用Perl語言定義的方式非常相似(就像Perl語言中的做法一樣,該方法理所當(dāng)然的運(yùn)行良好,但是,偶爾,也會出現(xiàn)可能不是你十分想要的那種結(jié)果,但你可以接受)。
EL為訪問對象屬性、集合元素、一組隱藏對象以及使用相關(guān)的、邏輯的和算術(shù)的操作符提供了支持。對于索引的屬性來說,包括數(shù)組和java.util.List類在內(nèi),元素可用下列語法訪問:
${alist[4]}
${aList[someVariable]} |
JavaBean屬性和java.util.Map元素(它代表一系列名字/值對)都可以使用下列方法的一個訪問得到。在以下的開頭兩個表達(dá)式里,我們可以訪問到一個用JavaBean語言命名為aProperty的屬性或者用關(guān)鍵字aProperty訪問到一個Map實體。在第三個表達(dá)式中(注意:我已經(jīng)省略了引號),我們用保存在變量aVariableContainingPropertyName中的一個名字訪問了在anObject之內(nèi)的一個元素:
${anObject.aProperty}
${anObject["aPropertyName"]}
${anObject[aVariableContainingPropertyName]} |
在EL中定義了許多隱藏變量:
● pageContext:用于該網(wǎng)頁的pageContext對象
● pageScope, requestScope, sessionScope, 和 applicationScope:這些是映射這些范圍的每一個變量到值上的Map集。
● param 和 paramValues:用頁訪問傳遞的參數(shù),與在JSP中一樣
● header 和 headerValues:用頁請求傳遞的頭,與在JSP中一樣
● cookie:Map映射cookie程序到特定的cookie對象上
EL定義了全套與你在Java中非常熟悉的那些完全對應(yīng)的操作符。算法操作符包括+、 -、 *、 / (或 div)、 和% (或 mod)。相關(guān)的操作符包括==、!=、<、>、<=、>=,它分別對應(yīng)于eq、ne、lt、gt、le、和ge。我不想詳細(xì)闡述這些操作符,因為他們都能自我說明。
JSTL標(biāo)記庫
至此,我已經(jīng)解釋了一些基本原理并且涵蓋了EL語法。我要專門討論四種JSTL標(biāo)記庫。我主要討論核心庫,因為它是你一定要使用的;但是,我也會詳細(xì)的討論其余幾個,以便你能啟動它。
首先,盡管我應(yīng)該更多地談?wù)揓STL標(biāo)記庫的兩種風(fēng)格,我上面提到的每個JSTL標(biāo)記庫以兩個版本出現(xiàn):一個支持使用EL的表達(dá)式;另一個支持標(biāo)準(zhǔn)JSP表達(dá)式。當(dāng)你引入任何標(biāo)記庫到一個JSP頁時,你要定義一個前綴,該前綴指定了與庫中標(biāo)記相對應(yīng)的命名空間。
四種標(biāo)準(zhǔn)標(biāo)記庫,和他們的JSTL規(guī)范定義的前綴常規(guī),列表如下。記住:你可以定義自己的前綴,但是這絕對沒有什么好處。
四種標(biāo)準(zhǔn)標(biāo)記庫

為了使用頁中的核心標(biāo)記庫( 實際上,你使你的頁對標(biāo)記庫的命名空間可見),在你的頁首包含下列例子中的指令:
<%@ taglib prefix="c" uri=http://java.sun.com/jstl/core %> |
為了使用該標(biāo)記庫的標(biāo)記,用你在你的包含語句中設(shè)計好的前綴,在你的頁中給每個標(biāo)記加上前綴:
<c:out value="${anExpression}"/> |
核心數(shù)據(jù)庫 讓我們更詳細(xì)的測試核心數(shù)據(jù)庫,先看看他最通用的功能。
顯示/設(shè)定值和異常處理 核心庫的最基本的標(biāo)記是c:out標(biāo)記,它在頁中顯示一個EL表達(dá)式的值。一個使用c:out的表達(dá)式通常是這樣:
We have <c:out value="${applicationScope.product.inventoryCount}"
escapeXml="true" default="0" /> of those items in stock. |
上例中,value屬性是我們送到頁輸出的表達(dá)式。我也說明了選項escapeXml的屬性和默認(rèn)屬性。escapeXml的屬性指定了XML字符(<、>、 &、 和 .)是否應(yīng)該轉(zhuǎn)化為相應(yīng)的字符實體代碼(這樣他們可以讓這些字符出現(xiàn)在一個HTML頁中);默認(rèn)屬性用于EL不能估算出數(shù)值或者數(shù)值計算出來為空的情況。
注意:當(dāng)EL支持完全貫穿整個JSP2.0的時候,你不需要使用c:out動作;你可以僅嵌套JSP表達(dá)式直接在頁中。
另一個普遍使用的核心動作是c:set,它在頁中設(shè)定一個變量。你可以用兩種方法使用c:set動作,第一種方法設(shè)定在var屬性中定義的變量到在value屬性中定義的值上,方法如下:
<c:set var="customerID" value="$param:customerNumber" scope="session" /> |
上述的選擇項scope屬性指定了我們要在會話期范圍內(nèi)設(shè)定變量customerID;如果范圍沒有制定,范圍默認(rèn)為頁。
c:set另一個強(qiáng)大的用途就是將c:set標(biāo)記的體的內(nèi)容賦給一個指定的變量:
<c:set var="cellContents">
<td>
<c:out value="${myCell}"/>
</td>
</c:set> |
在上例中,c:set動作定義了名為cellContents(在頁的范圍內(nèi))的變量,該變量擁有在標(biāo)記的體中定義的內(nèi)容。在這種情況下,體定義了一個HTML表格單元格元素,計算體中的c:out動作,并且該計算的結(jié)果包括在體中的字符串直接量值。
就像你預(yù)想的那樣,JSTL進(jìn)行異常處理有點早。在典型的JSP頁中,你有兩種異常處理的方法:試圖/捕捉直接嵌入到頁中的小腳本代碼中的塊;或者用一個JSP errorPage指令。JSP errorPage指令提供一個良好的捕捉所有異常的方法來處理頁中任何可能的異常。JSTL用一個c:catch動作提供一個好的可選擇的方式。這個c:catch動作提供一個更細(xì)粒度的有效方法來處理異常。而且沒有嵌入Java代碼到頁中。一個c:catch動作是這樣的:
<c:catch>
<!--. . . some set of nested JSTL tags below which would be hit on an exception-->
</c:catch> |
c:catch動作有一個可選的屬性,即一個指向拋出異常的變量。
你可能不怎么愿意使用c:remove標(biāo)記。這個標(biāo)記有用于變量名和范圍的屬性,并且可從指定的范圍內(nèi)刪除指定的變量。
流程控制
讓我們轉(zhuǎn)為討論JSTL的流程控制和條件標(biāo)記。如果你已經(jīng)用任何一種語言使用了條件和流程控制語句,理論上這里沒什么新鮮的東西。
c:if動作處理簡單條件語句的測試。計算測試屬性中的Boolean表達(dá)式的值,如果表達(dá)式為真的話,計算體的內(nèi)容。在下面的動作中,我們也說明了備選項var屬性。為了以后的使用,var屬性保存測試結(jié)果在頁(如果沒有指定其他scope屬性的話)中。
<c:if test="${status.totalVisits == 1000000}" var="visits">
You are the millionth visitor to our site! Congratulations!
</c:if> |
下面我們展示了用c:choose、 c:when、 和 c:otherwise交換邏輯的JSTL的支持。一組c:when動作可能包括在一個備選的標(biāo)記內(nèi),如果在c:when塊中任何表達(dá)式計算值為真的話,就不用計算c:choose動作內(nèi)的測試。如果c:when塊中沒有一個測試計算值為真的時候:如果出現(xiàn)c:otherwise動作內(nèi)容時,則計算c:otherwise動作的內(nèi)容:
<c:choose>
<c:when test="${item.type == 'book'}">
...
</c:when>
<c:when test="${item.type == 'electronics'}">
...
</c:when>
<c:when test="${item.type == 'toy'}">
...
</c:when>
<c:otherwise>
...
</c:otherwise>
</c:choose> |
c:foreach動作提供一個容易的方法來迭代一個集合的元素。如果你想只迭代集合的一部分的話,你可以分別用begin、 end、 和 step屬性指定起點、終點和一個遞增值。在下面的例子中,我們在變量customerNames中迭代一個集合的內(nèi)容;在每個循環(huán)中,下一個元素輸入到變量名內(nèi)并在c:foreach動作的體內(nèi)計算:
<table>
<c:forEach var="name" items="${customerNames}">
<tr><td><c:out value="${name}"/></td></tr>
</c:forEach>
</table> |
記得Java的StringTokenizer類嗎?有了c:forTokens動作,你可以用JSTL獲得類似的功能。這個程序片斷可使用在delims屬性中定義的定界符通過items String屬性中的條目迭代。注意,items 屬性不必是一個字符直接量;它可以是任何有效的EL表達(dá)式:
<table>
<c:forTokens items="47,52,53,55,46,22,16,2" delim="," var="dailyPrice">
<tr><td><c:out value="${dailyPrice}"/></td></tr>
</c:forTokens>
</table> |
在接下來的完整的JSTL頁中,我列出了已經(jīng)傳遞到該頁的所有參數(shù)。param 和paramValues對象是映射關(guān)鍵字到一個或多個值的Java Map集。在本例中,我們找出了用于集合的每個MapEntry的關(guān)鍵字即參數(shù)名,并且使用關(guān)鍵字來查找所有與關(guān)鍵字關(guān)聯(lián)的參數(shù)值:
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<html>
<body>
<head>
<title>Parameter Listing Example</title>
</head>
<br>
<b>Parameter values passed to this page for each parameter: </b>
<table border="2">
<c:forEach var="current" items="${param}">
<tr>
<td>
<b><c:out value="${current.key}" /></b>
</td>
<c:forEach var="aVal" items="${paramValues[current.key]}">
<td>
<c:out value="${aVal}" />
</td>
</c:forEach>
</tr>
</c:forEach>
</table>
</body>
</html> |
其他動作 我們也需要討論一些其他重要的核心標(biāo)記庫動作。在網(wǎng)頁實現(xiàn)中一個潛在的問題范圍與URL編碼有關(guān)。沒有URL編碼,在網(wǎng)頁中傳遞的URL的某個字符如空格,可能會迷惑Web服務(wù)器。URL編碼確保這些特殊的字符用不引起迷惑的字符代替。下列例子在變量myUrl中定義了一個URL,該變量myUrl由一個URL和一系列參數(shù)組成。URL動作(注意,這里只在JSTL意義上的動作)保證所有字符都正確的編碼:
<c:url value="http://acme.com/exec/register" var="myUrl">
<c:param name="name" value="${param.name}"/>
<c:param name="country" value="${param.country}"/>
</c:url>
<a href='<c:out value="${myUrl}"/>'>Register</a> |
在上面的代碼中,param動作簡單的定義了一組名值對。
JSP允許設(shè)計者用 <JSP:INCLUDE></JSP:INCLUDE>
<JSP:INCLUDE>指令包括其他頁的內(nèi)容。JSTL用JSTL c:import動作擴(kuò)展了這個概念。c:import的主要優(yōu)勢是:你能指定一個任意的URL;也可以包含你的Web應(yīng)用之外(W3C的任何地方)的頁的內(nèi)容或者你的服務(wù)器上的另一個Web應(yīng)用內(nèi)的頁的內(nèi)容。
對于一組與HTTP相關(guān)的標(biāo)記來說,沒有一個方法處理HTTP重定向是不完整的。JSTL用c:redirect動作支持它。
國際化標(biāo)記庫
在討論了JSTL支持國際化中,我假設(shè)你已經(jīng)有了對下列主題的合理的理解:
●資源包和Java如何找到它們
●Locale類
●資源包查找和MessageFormat類
●局部編碼以及它與Java國際化的關(guān)系
●Java文本和數(shù)據(jù)格式化
如果你需要這些主題的更多的信息,請閱讀Resources (http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl-p4.html#resources#resources)上的參考部分與國際化有關(guān)的參考文章。
我在下面的兩個部分討論了國際化標(biāo)記庫。在第一部分,我包括了你可能使用的格式化和解釋相關(guān)的動作(它最初對應(yīng)于java.text封裝包中的類),盡管你沒有開發(fā)過完全的國際化應(yīng)用程序,可能你也要用到它。在第二部分,我說明了更針對于國際化的動作。
如果存在一個遠(yuǎn)程的可能性,你的應(yīng)用程序會在你本國之外使用。如果你從一開始就建立在國際化的支持上,那么開發(fā)就會更加容易。無論你使用何種方法來開發(fā),即使像JSTL一樣容易使用的方法,一開始建立在國際化支持上也是十分有益的。
格式標(biāo)記庫:格式動作
如果你已經(jīng)使用了Java的 DateFormat 和NumberFormat類,在下面的標(biāo)記中使用的方法應(yīng)該看起來眼熟,因為JSTL格式化動作是在這些類之上建立的。這些Java類普遍提供一個format( )函數(shù),它將一個Java類型轉(zhuǎn)化為一個格式化的String和創(chuàng)建對應(yīng)于該String的Java對象。
fmt:formatNumber動作有一個value屬性和一個pattern屬性。value屬性是一個與我們看到的其它value屬性類似的EL表達(dá)式或者變量。pattern屬性與在NumberFormat類中定義的模式一樣。下列動作發(fā)送一個格式化的String到JSP頁的輸出上:
<fmt:formatNumber value="1000.001" pattern="#,#00.0#"/> |
在這個fmt:formatNumber動作里,我們使用了type屬性來指定:格式化我們需要格式化的值為一個貨幣值。我們保存格式化的結(jié)果在一個變量名為dollars中。在一個美國的locale類中,下面的程序生成一個字符串$3456.79(注意,它將使用的貨幣值四舍五入了):
<fmt:formatNumber value="3456.789" type="currency" var="dollars"/> |
用于上面的type屬性的可能值包括currency、 number、 和percent。
在例中,我們使用了另一種方法——使用了一個包括在value屬性中的一個格式化的域(currency,每type屬性)的一個文本字符串。并且解析它來獲取一個數(shù)字。結(jié)果儲存在由var屬性指定的一個變量內(nèi)。盡管這個屬性是可選的,還是會經(jīng)常使用它。另外,解析的值送到頁輸出上:
<fmt:parseNumber value="${currencyInput}" type="currency"
var="parsedNumber"/>
fmt:formatDate動作有一個value屬性、一個format屬性、
一個指向處理格式化的格式類的屬性(典型的,如java.util.Date):
<jsp:useBean id="now" class="java.util.Date" />
<fmt:formatDate value="${now}" timeStyle="long"
dateStyle="long"/> |
像數(shù)字格式化一樣,JSTL頁提供一個機(jī)制來解析一個代表日期的、進(jìn)入Date對象的字符串:
<fmt:parseDate value="${dateInput}" pattern="MM dd, YYYY" /> |
查看java.util.DateFormat類,了解如何處理格式化和模式的更多詳細(xì)的資料。
格式標(biāo)記庫:國際化動作 Java本地化的一個關(guān)鍵點是ResourceBundle類。JSTL動作允許你用這個類簡單的工作。這個例子使用了fmt:bundle動作來得到與當(dāng)前Locale 和 fmt:message動作對應(yīng)的一個ResourceBundle動作,從而查看該資源包中的本地化的字符串:
<fmt:bundle basename="myBundle">
<%-- Use values in myBundle --%>
<fmt:message key="Introduction">
<fmt:param value="${loginName}"/>
<fmt:param value="${loginCount}"/>
</fmt:message>
<fmt:formatDate value="${now}" var="parsedDate"/>
</fmt:bundle> |
經(jīng)常,fmt:message動作簡單的查看了對應(yīng)于一個關(guān)鍵詞的字符串。在上面的例子中,在ResourceBundle中的字符串包含了取代兩個值的占位符。這些值在fmt:param動作中定義過,這就像Java MessageFormat類使用的方法。
有一些類似的動作可指定一個時區(qū),時區(qū)可應(yīng)用到在標(biāo)記的體中計算的任何事上:
<fmt:timeZone value="someTimeZone">
<!-- actions in this context will be evaluated using someTimeZone -->
</fmt:timeZone> |
上述的fmt:bundle 和 fmt:timeZone動作有分別對應(yīng)的fmt:setBundle和fmt:setTimeZone的動作。這些動作添加了可選的scope屬性;因此,你可以使用這些動作在任何等同于應(yīng)用范圍的范圍內(nèi)設(shè)定一個資源捆綁或者一個時區(qū)。
如果你用非歐洲的locale類工作的話,你可能得擔(dān)心編碼的問題,JSTL支持用fmt:requestEncoding動作編碼。
SQL標(biāo)記庫 JSTL 允許容易的數(shù)據(jù)庫的集成。但是,值得注意的是:沙箱之外的JSTL的執(zhí)行有一些限制。主要的問題與連接池有關(guān)。建立并維護(hù)到數(shù)據(jù)庫的連接是很消耗資源的。JSTL SQL動作使得許多數(shù)據(jù)庫連接建立起來,通常的,每個用戶至少有一個。因此,JSTL SQL標(biāo)記對于原型開發(fā)的或低容量的、基于Web的應(yīng)用程序意義重大。但是它不適合于大規(guī)模的應(yīng)用程序。一個可縮放的產(chǎn)品應(yīng)用程序一般是在一個頁面內(nèi)處理數(shù)據(jù)庫訪問如隱藏數(shù)據(jù)庫訪問和處理像連接池之類的細(xì)節(jié)。但是,還是有方法允許你實現(xiàn)連接池和用一點自定義碼來使用JSTL SQL動作(請看:"JSTL 1.0: What JSP Applications Need, Part 2" in Resources :http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl-p5.html#resources#resources).
我們看一些簡單的例子。這些例子使用來自SQL庫的JSTL標(biāo)記。如果你熟悉SQL基礎(chǔ)的話,你應(yīng)該能夠改編這些例子用在你的應(yīng)用程序上。
在下面的程序片斷中,我們建立了一條到一個數(shù)據(jù)庫的連接,選擇一組匹配一個訂單ID的訂單項目,并且在一個表格中顯示item屬性:
<sql:setDataSource
driver="com.cheapDrivers.jdbcDriver"
url="jdbc:cheapDrivers:."
user="guest"
password="password"
var="dataSource" />
<sql:query var="orderItems" dataSource="${dataSource}">
SELECT * FROM items
WHERE order_id = <cout value="${orderID}"/>
ORDER BY price
</sql:query>
<table>
<c:forEach var="row" items="${orderItems.rows}">
<tr>
<td><c:out value="${row.itemName}"/></td>
<td><c:out value="${row.price}"/></td>
<td><c:out value="${row.weight}"/></td>
</tr>
</c:forEach>
</table> |
在下一個例子中,我會說明JSTL是如何支持?jǐn)?shù)據(jù)庫事務(wù)的,在數(shù)據(jù)庫中,許多包含對表格多個改動的操作必須是對所有的,或者什么也不做:如果出現(xiàn)一個問題時,改動必須為空操作。在下面例子中,sql:update動作包含在一個sql:transaction動作內(nèi),如果在事務(wù)處理過程中,存在任何SQL錯誤的話,在事務(wù)范圍內(nèi)執(zhí)行的所有操作都得重來。
sql:update動作的命名有一點誤導(dǎo):除了SQL UPDATE外,sql:update也支持INSERT 和 DELETE,甚至是SQL CREATE。實際上,它支持不產(chǎn)生結(jié)果的任何一條SQL操作。在下面的例子中,sql:update通過插入變量值到一個PreparedStatement中來執(zhí)行一個UPDATE動作。在這個代碼片斷中,我們在兩個賬戶之間傳送錢(需要預(yù)先包裝在一個事務(wù)中的某些東西的一個經(jīng)典的例子):
<sql:transaction dataSource="${dataSource}">
<sql:update>
UPDATE account
SET account_balance =account_balance -?
WHERE accountNo = ?
<sql:param value="${transferAmount}"/>
<sql:param value="${sourceAccount}"/>
</sql:update>
<sql:update>
UPDATE account
SET account_balance =account_balance +?
WHERE accountNo = ?
<sql:param value="${transferAmount}"/>
<sql:param value="${destAccount}"/>
</sql:update>
</sql:transaction> |
XML標(biāo)記庫 使用標(biāo)準(zhǔn)的XML標(biāo)記庫,你所能做的完整的處理,特別是可擴(kuò)展性單一語言變換說明(XSLT)的處理,對于另一篇文章來說是一個很好的主題。下面我會涵蓋足夠的讓你開始的知識。
在JSTL的XML支持遵循XPath規(guī)范。XPath的重要功能之一是為訪問XML著名的分層的信息提供一個清晰的語法。也許看到每件是如何工作的最容易的方法就是看看我們?nèi)绾问褂脕碜杂谝粋€真實JSTL頁的一個片斷里的標(biāo)記:
!-- Find and parse our XML document (somewhere on the WWW) -->
<c:import url="http://www.cheapstuff.com/orderStatus?id=2435" var="xml"/>
<x:parse xml="${xml}" var="doc"/>
<!-- access XML data via XPath expressions -->
<x:out select="$doc/name"/>
<x:out select="$doc/shippingAddress"/>
<x:out select="$doc/deliveryDate"/>
<!-- Set a scoped variable -->
<x:set var="custName" scope="request" select="$doc/name"/> |
在上面的輸入和解析動作中,我們裝載和解析了一個指定的XML文檔到一個變量doc里。在上面的每個x:out動作中,我們使用了一個XPath表達(dá)式訪問了解析的XML文檔的元素,并且發(fā)送結(jié)果到JSP頁輸出中。
上面的設(shè)置表達(dá)式計算了一個XPath表達(dá)式并且將結(jié)果輸入到一個限定范圍的變量里(在上述例子中,它指在一個請求范圍內(nèi))。
x:out 和x:set動作可以輸出一個JspTagException。如果他們沒有成功的話(極有可能因為XPath表達(dá)式指向并不存在的標(biāo)記),你的頁,像在所有其他情況下一樣,應(yīng)該智能的處理這些異常(要么通過傳統(tǒng)的JSP errorPage指令,要么使用JSTL的c:catch動作),
JSTL容易處理XSLT轉(zhuǎn)換。在下面的示范頁中,我們使用來自XML標(biāo)記庫中的x:transform動作,用一個XSLT stylesheet創(chuàng)建一個來自于XML源文檔的一個格式化的頁。x:transform動作最重要的屬性是XML和XSLT屬性,在下面的例子中,我們設(shè)置了一個來自于我們在同一頁初始化的變量的xslt屬性;我們也在動作的體中設(shè)置了XML屬性。該動作默認(rèn)為x:transform動作。
默認(rèn)的,轉(zhuǎn)換的結(jié)果送到頁輸出上;你也可以保存結(jié)果到一個帶x:var屬性的變量內(nèi):
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/xml" prefix="x" %>
<c:set var="xsltSource">
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="music">
<html>
<head></head>
<body marginheight="0" marginwidth="0" topmargin="0" leftmargin="0">
<table cellpadding="0" cellspacing="0" border="1" bgcolor="#ffffff">
<tr>
<td><STRONG>Artist</STRONG></td>
<td><STRONG>Album</STRONG></td>
<td><STRONG>Year</STRONG></td>
<td><STRONG>Genre</STRONG></td>
</tr>
<!---Set up for loop to collect all the artist information //-->
<!-- <xsl:for-each select="./*[name()='artists']"> -->
<xsl:for-each select="artists">
<tr>
<td><xsl:value-of select="artist"/></td>
<td><xsl:value-of select="album"/></td>
<td><xsl:value-of select="year"/></td>
<td><xsl:value-of select="genre"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
</c:set>
<x:transform xslt="${xsltSource}" >
<music>
<artists>
<artist>Jonny B</artist>
<album>Feedback and Distortion</album>
<year>2001</year>
<genre>Rock</genre>
</artists>
<artists>
<artist>Harmony's Nieces</artist>
<album>Sappy Pop Ballads</album>
<year>2002</year>
<genre>Pop</genre>
</artists>
</music>
</x:transform> |
你也能使用c:import動作再定義一個額外的源文檔和stylesheet,就好像在這個示范的代碼片斷展示的一樣::
<c:import var="${xmlSource}" url="${someDocumentURL}" />
<c:import var="${xsltSource}" url="${anotherDocumentURL}" />
<x:transform xml="${xmlSource}" xslt="${xsltSource}" > |
結(jié)束語 至此,你應(yīng)該對JSTL、它的四種標(biāo)準(zhǔn)標(biāo)記庫、和它如何使網(wǎng)頁開發(fā)更為容易 有了很好的理解。現(xiàn)在你開始寫一些JSTL!
【
關(guān)于作者】
Steve Small從事Java開發(fā)多年,一直處于技術(shù)領(lǐng)導(dǎo)層和開發(fā)位置上。他先后為Boeing、 Amazon.com工作過,目前在沃什灣西雅圖的PictureIQ公司供職。從1998年起,在華盛頓大學(xué)兼職開發(fā)和教學(xué)Java課程。
http://www.javaworld.com/feedback