JSTL教程- -
??????????????????????????????????????
JSP 標準標記庫(JSP Standard Tag Library,JSTL)是一個實現(xiàn) Web 應(yīng)用程序中常見的通用功能的定制標記庫集,這些功能包括迭代和條件判斷、數(shù)據(jù)管理格式化、XML 操作以及數(shù)據(jù)庫訪問。在 developerWorks 上其新系列的第一篇文章中,軟件工程師 Mark Kolb 向您展示了如何使用 JSTL 標記來避免在 JSP 頁面中使用腳本編制元素。您還將了解如何通過從表示層刪除源代碼來簡化軟件維護。最后,您將了解 JSTL 經(jīng)過簡化的表達式語言,它允許在不必使用功能齊全的編程語言的情況下對 JSTL 操作指定動態(tài)屬性值。
JavaServer Pages(JSP)是用于 J2EE 平臺的標準表示層技術(shù)。JSP 技術(shù)提供了用于執(zhí)行計算(這些計算用來動態(tài)地生成頁面內(nèi)容)的腳本編制元素和操作。腳本編制元素允許在 JSP 頁面中包括程序源代碼,在為響應(yīng)用戶請求而呈現(xiàn)頁面時可以執(zhí)行這些源代碼。操作將計算操作封裝到很象 HTML 或 XML 標記的標記中,JSP 頁面的模板文本通常包含這些標記。JSP 規(guī)范只將幾種操作定義成了標準,但從 JSP 1.1 開始,開發(fā)人員已經(jīng)能夠以定制標記庫的方式創(chuàng)建其自己的操作了。
JSP 標準標記庫(JSTL)是 JSP 1.2 定制標記庫集,這些標記庫實現(xiàn)大量服務(wù)器端 Java 應(yīng)用程序常用的基本功能。通過為典型表示層任務(wù)(如數(shù)據(jù)格式化和迭代或條件內(nèi)容)提供標準實現(xiàn),JSTL 使 JSP 作者可以專注于特定于應(yīng)用程序的開發(fā)需求,而不是為這些通用操作“另起爐灶”。
當(dāng)然,您可以使用 JSP 腳本編制元素(scriptlet、表達式和聲明)來實現(xiàn)此類任務(wù)。例如,可以使用三個 scriptlet 實現(xiàn)條件內(nèi)容,清單 1 中著重顯示了這三個 scriptlet。但是,因為腳本編制元素依賴于在頁面中嵌入程序源代碼(通常是 Java 代碼),所以對于使用這些腳本編制元素的 JSP 頁面,其軟件維護任務(wù)的復(fù)雜度大大增加了。例如,清單 1 中的 scriptlet 示例嚴格地依賴于花括號的正確匹配。如果不經(jīng)意間引入了一個語法錯誤,則條件內(nèi)容中的嵌套其它 scriptlet 可能會造成嚴重破壞,并且在 JSP 容器編譯該頁面時,要使所產(chǎn)生的錯誤信息有意義可能會很困難。
|
JSTL 1.0
兩個 JSTL 標記庫驗證器允許開發(fā)人員在其 JSP 應(yīng)用程序中強制使用編碼標準。可以配置 ScriptFreeTLV 驗證器以在 JSP 頁面中禁用各種類型的 JSP 腳本元素 — scriptlet、表達式和聲明。類似地,PermittedTaglibsTLV 驗證器可以用來限制可能由應(yīng)用程序的 JSP 頁面訪問的定制標記庫集(包括 JSTL 標記庫)。
盡管 JSTL 最終將會成為 J2EE 平臺的必需組件,但目前只有少數(shù)應(yīng)用程序服務(wù)器包括它。JSTL 1.0 的參考實現(xiàn)可作為 Apache 軟件基金會(Apache Software Foundation)的 Jakarta Taglibs 項目(請參閱參考資料)的一部分而獲得。可以將該參考實現(xiàn)中的定制標記庫合并到任何支持 JSP 1.2 和 Servlet 2.3 規(guī)范的服務(wù)器,以添加對 JSTL 的支持。
表達式語言
清單 2. 合并請求時屬性值的 JSP 操作
|
清單 3. 說明 EL 表達式定界符的 JSTL 操作
<c:out value="${user.firstName}"/>
清單 4. 組合靜態(tài)文本和多個 EL 表達式以指定動態(tài)屬性值
<c:out value="Hello ${user.firstName} ${user.lastName}"/>
限制了作用域的變量
·頁面作用域
·請求作用域
·會話作用域
·應(yīng)用程序作用域
通過將字符串映射為期望作用域中的對象來將對象存儲到該作用域。然后,就可以通過提供相同字符串來從該作用域檢索該對象。在作用域的映射中查找字符串,并返回被映射的對象。在 Servlet API 中,將此類對象稱為相應(yīng)作用域的屬性。但是,在 EL 的上下文中,也將與屬性相關(guān)聯(lián)的字符串看作變量的名稱,該變量通過屬性映射的方式獲得特定的值。
在 EL 中,與隱式對象無關(guān)聯(lián)的標識符被認為是存儲在四個 JSP 作用域中的名稱對象。首先對頁面作用域檢查是否存在這樣的標識符,其次對請求作用域、然后對會話作用域、最后對應(yīng)用程序作用域依次進行這樣的檢查,然后測試該標識符的名稱是否與存儲在該作用域中的某個對象的名稱匹配。第一個這樣的匹配作為 EL 標識符的值被返回。通過這種方法,可以將 EL 標識符看作引用限制了作用域的變量。
從更技術(shù)的方面來說,沒有映射到隱式對象的標識符是用 PageContext 實例的 findAttribute() 方法求值的,該實例表示對頁面的處理,在該頁面上,當(dāng)前正在處理用于請求的表達式。標識符的名稱作為參數(shù)傳遞給這個方法,然后該方法依次在四個作用域中搜索具有相同名稱的屬性。并將所找到的第一個匹配項作為 findAttribute() 方法的值返回。如果未在這四個作用域中找到這樣的屬性,則返回 null。
最終,限制了作用域的變量是四個 JSP 作用域的屬性,這些屬性具有可以用作 EL 標識符的名稱。只要對限制了作用域的變量賦予由字母數(shù)字組成的名稱,就可以通過 JSP 中提供的用于設(shè)置屬性的任何機制來創(chuàng)建它們。這包括內(nèi)置的 <jsp:useBean> 操作,以及由 Servlet API 中的幾個類定義的 setAttribute() 方法。此外,四個 JSTL 庫中定義的許多定制標記本身就能夠設(shè)置作為限制了作用域的變量使用的屬性值。
隱式對象
表 1 中列出了 11 個 EL 隱式對象的標識符。不要將這些對象與 JSP 隱式對象(一共只有九個)混淆,其中只有一個對象是它們所共有的。
表 1. EL 隱式對象
類別 標識符 描述
JSP pageContext PageContext 實例對應(yīng)于當(dāng)前頁面的處理
作用域 pageScope 與頁面作用域?qū)傩缘拿Q和值相關(guān)聯(lián)的 Map 類
requestScope 與請求作用域?qū)傩缘拿Q和值相關(guān)聯(lián)的 Map 類
sessionScope 與會話作用域?qū)傩缘拿Q和值相關(guān)聯(lián)的 Map 類
applicationScope 與應(yīng)用程序作用域?qū)傩缘拿Q和值相關(guān)聯(lián)的 Map 類
請求參數(shù) param 按名稱存儲請求參數(shù)的主要值的 Map 類
paramValues 將請求參數(shù)的所有值作為 String 數(shù)組存儲的 Map 類
請求頭 header 按名稱存儲請求頭主要值的 Map 類
headerValues 將請求頭的所有值作為 String 數(shù)組存儲的 Map 類
Cookie cookie 按名稱存儲請求附帶的 cookie 的 Map 類
初始化參數(shù) initParam 按名稱存儲 Web 應(yīng)用程序上下文初始化參數(shù)的 Map 類
其余所有 EL 隱式對象都是映射,可以用來查找對應(yīng)于名稱的對象。前四個映射表示先前討論的各種屬性作用域。可以用它們來查找特定作用域中的標識符,而不用依賴于 EL 在缺省情況下使用的順序查找過程。
接下來的四個映射用來獲取請求參數(shù)和請求頭的值。因為 HTTP 協(xié)議允許請求參數(shù)和請求頭具有多個值,所以它們各有一對映射。每對中的第一個映射返回請求參數(shù)或頭的主要值,通常是恰巧在實際請求中首先指定的那個值。每對中第二個映射允許檢索參數(shù)或頭的所有值。這些映射中的鍵是參數(shù)或頭的名稱,但這些值是 String 對象的數(shù)組,其中的每個元素都是單一參數(shù)值或頭值。
cookie 隱式對象提供了對由請求設(shè)置的 cookie 名稱的訪問。這個對象將所有與請求相關(guān)聯(lián)的 cookie 名稱映射到表示那些 cookie 特性的 Cookie 對象。
最后一個 EL 隱式對象 initParam 是一個映射,它儲存與 Web 應(yīng)用程序相關(guān)聯(lián)的所有上下文的初始化參數(shù)的名稱和值。初始化參數(shù)是通過 web.xml 部署描述符文件指定的,該文件位于應(yīng)用程序的 WEB-INF 目錄中。
存取器
結(jié)果是,對這些對象的特性或(在對象是數(shù)組和集合的情況下)對其元素的訪問通常是令人滿意的。就為了實現(xiàn)這種用途,EL 提供了兩種不同的存取器(點運算符(.)和方括號運算符([])),也支持通過 EL 操作特性和元素。
點運算符通常用于訪問對象的特性。例如,在表達式 ${user.firstName} 中,使用點運算符來訪問 user 標識符所引用對象的名為 firstName 的特性。EL 使用 Java bean 約定訪問對象特性,因此必須定義這個特性的 getter 方法(通常是名為 getFirstName() 的方法),以便表達式正確求值。當(dāng)被訪問的特性本身是對象時,可以遞歸地應(yīng)用點運算符。例如,如果我們虛構(gòu)的 user 對象有一個實現(xiàn)為 Java 對象的 address 特性,那么也可以用點運算符來訪問這個對象的特性。例如,表達式 ${user.address.city} 將會返回這個地址對象嵌套的 city 特性。
方括號運算符用來檢索數(shù)組和集合的元素。在數(shù)組和有序集合(也即,實現(xiàn)了 java.util.List 接口的集合)的情況下,把要檢索的元素的下標放在方括號中。例如,表達式 ${urls[3]} 返回 urls 標識符所引用的數(shù)組或集合的第四個元素(和 Java 語言以及 JavaScript 中一樣,EL 中的下標是從零開始的)。
對于實現(xiàn) java.util.Map 接口的集合,方括號運算符使用關(guān)聯(lián)的鍵查找存儲在映射中的值。在方括號中指定鍵,并將相應(yīng)的值作為表達式的值返回。例如,表達式 ${commands["dir"]} 返回與 commands 標識符所引用的 Map 中的 "dir" 鍵相關(guān)聯(lián)的值。
對于上述兩種情況,都可允許表達式出現(xiàn)在方括號中。對嵌套表達式求值的結(jié)果將被作為下標或鍵,用來檢索集合或數(shù)組的適當(dāng)元素。和點運算符一樣,方括號運算符也可以遞歸應(yīng)用。這使得 EL 能夠從多維數(shù)組、嵌套集合或兩者的任意組合中檢索元素。此外,點運算符和方括號運算符還可以互操作。例如,如果數(shù)組的元素本身是對象,則可以使用方括號運算符來檢索該數(shù)組的元素,并結(jié)合點運算符來檢索該元素的一個特性(例如 ${urls[3].protocol})。
假定 EL 充當(dāng)指定動態(tài)屬性值的簡化語言,EL 存取器有一個有趣的功能(與 Java 語言的存取器不同),那就是它們在應(yīng)用于 null 時不拋出異常。如果應(yīng)用 EL 存取器的對象(例如,${foo.bar} 和 ${foo["bar"]} 中的 foo 標識符)是 null,那么應(yīng)用存取器的結(jié)果也是 null。事實證明,在大多數(shù)情況下,這是一個相當(dāng)有用的行為,不久您就會了解這一點。
最后,點運算符和方括號運算符可能實現(xiàn)某種程度的互換。例如,也可以使用 ${user["firstName"]} 來檢索 user 對象的 firstName 特性,正如可以用 ${commands.dir} 獲取與 commands 映射中的 "dir" 鍵相關(guān)聯(lián)的值一樣。
運算符
最終,EL 還包括了幾個用來操作和比較 EL 表達式所訪問數(shù)據(jù)的運算符。表 2 中匯總了這些運算符。
表 2. EL 運算符
類別 運算符
算術(shù)運算符 +、-、*、/(或 div)和 %(或 mod)
關(guān)系運算符 ==(或 eq)、!=(或 ne)、<</code>(或 lt)、>(或 gt)、<=(或 le)和 >=(或 ge)
邏輯運算符 &&(或 and)、||(或 or)和 !(或 not)
驗證運算符 empty
算術(shù)運算符支持數(shù)值的加法、減法、乘法和除法。還提供了一個求余運算符。注:除法和求余運算符都有替代的、非符號的名稱(為的是與 XPath 保持一致)。清單 5 中顯示了一個演示算術(shù)運算符用法的示例表達式。對幾個 EL 表達式應(yīng)用算術(shù)運算符的結(jié)果是將該算術(shù)運算符應(yīng)用于這些表達式返回的數(shù)值所得的結(jié)果。
清單 5. 利用算術(shù)運算符的 EL 表達式
|
關(guān)系運算符允許比較數(shù)字或文本數(shù)據(jù)。比較的結(jié)果作為布爾值返回。邏輯運算符允許合并布爾值,返回新的布爾值。因此,可以將 EL 邏輯運算符應(yīng)用于嵌套的關(guān)系或邏輯運算符的結(jié)果,如清單 6 所示。
清單 6. 利用關(guān)系和邏輯運算符的 EL 表達式
|
最后一種 EL 運算符是 empty,它對于驗證數(shù)據(jù)特別有用。empty 運算符采用單個表達式作為其變量(也即,${empty input}),并返回一個布爾值,該布爾值表示對表達式求值的結(jié)果是不是“空”值。求值結(jié)果為 null 的表達式被認為是空,即無元素的集合或數(shù)組。如果參數(shù)是對長度為零的 String 求值所得的結(jié)果,則 empty 運算符也將返回 true。
表 3 顯示了 EL 運算符的優(yōu)先級。正如清單 5 和 6 所示,可以用圓括號對表達式分組,高于普通的優(yōu)先級規(guī)則。
表 3. EL 運算符優(yōu)先級(自頂?shù)降祝瑥淖蟮接遥?
|
文字
在 EL 表達式中,數(shù)字、字符串、布爾值和 null 都可以被指定為文字值。字符串可以用單引號或雙引號定界。布爾值被指定為 true 和 false。
Taglib 偽指令
正如我們先前討論的,JSTL 1.0 包括四個定制標記庫。為了演示 JSTL 標記和表達式語言的交互,我們將研究幾個來自 JSTL core 庫的標記。和使用任何 JSP 定制標記庫一樣,必須在您想要使用這個庫標記的任何頁面中包括 taglib 偽指令。清單 7 顯示了用于這個特定庫的偽指令。
清單 7. 用于 JSTL core 庫 EL 版本的 taglib 偽指令
|
清單 8. 用于 JSTL core 庫 RT 版本的 taglib 偽指令
|
變量標記
清單 9. <c:set> 操作的語法
|
清單 10 中顯示了 <c:set> 的兩個示例。在第一個示例中,將會話作用域變量設(shè)置成 String 值。在第二個示例中,用表達式來設(shè)置數(shù)值:將頁面作用域內(nèi)名為 square 的變量賦值為名為 x 的請求參數(shù)的值的平方。
清單 10. <c:set> 操作示例
|
清單 11. 通過主體內(nèi)容指定 <c:set> 操作的值
|
清單 12. <c:remove> 操作示例
|
輸出
清單 13. <c:out> 操作的語法
|
例如,假定有一個名為 user 的會話作用域變量,它是一個類的實例,該類為用戶定義了兩個特性:username 和 company。每當(dāng)用戶訪問站點時,這個對象被自動分配給會話,但直到用戶實際登錄后,才會設(shè)置這兩個特性。假定是這種方案,請考慮清單 14 中的 JSP 片段。在用戶登錄之后,這個片段將顯示單詞“Hello”,其后是他/她的用戶名和一個驚嘆號。但是,在用戶登錄之前,由這個片段生成的內(nèi)容則是短語“Hello Guest!”。在這種情況下,因為 username 特性還有待初始化,所以 <c:out> 標記將轉(zhuǎn)而打印出 default 屬性的值(即字符串“Guest”)。
清單 14. 帶缺省內(nèi)容的 <c:out> 操作示例 Hello
|
清單 15. 禁用轉(zhuǎn)義的 <c:out> 操作示例
|
用缺省值設(shè)置變量
清單 16 中說明了這種方法。外部 <c:set> 標記的行為非常簡單:它根據(jù)其主體內(nèi)容設(shè)置會話作用域 timezone 變量的值。但是,在這種情況下,主體內(nèi)容是通過 <c:out> 操作生成的。這個嵌套操作的值屬性是表達式 ${cookie['tzPref'].value},它嘗試通過 cookie 隱式對象返回名為 tzPref 的 cookie 值。(cookie 隱式對象將 cookie 名稱映射到相應(yīng)的 Cookie 實例,這意味著必須通過對象的 value 特性使用點運算符來檢索儲存在 cookie 中的實際數(shù)據(jù)。)
清單 16. 合并 <c:set> 和 <c:out> 以提供缺省變量值
|
EL 和 JSP 2.0
這個 JSP 2.0 功能(就象 JSTL 本身一樣)將支持頁面作者進一步減少對 JSP 編制腳本元素的依賴,從而改進 JSP 應(yīng)用程序的可維護性。
結(jié)束語
清單 17. 合并 <c:set> 和 <c:out> 以提供缺省變量值
|
BR>