value='<%= request.getParameter("timezone") %>'/>
因為請求時屬性值是用表達式指定的,所以它們往往有和其它腳本元素一樣的軟體維護問題。因此,JSTL 定製標記支持另一種用於指定動態屬性值的機制。可以用簡化的表達式語言(EL)而不使用完整的 JSP 表達式來指定 JSTL 操作的屬性值。EL 提供了一些標識符、存取器和運算符,用來檢索和操作駐留在 JSP容器中的數據。EL 在某種程度上以 EcmaScript(請參閱 參考資料)和 XML 路徑語言(XML Path Language,XPath)為基礎,因此頁面設計人員和程序員都應該熟悉它的語法。EL 擅長尋找對象及其特性,然後對它們執行簡單操作;它不是編程語言,甚至不是腳本編製語言。但是,與 JSTL 標記一起使用時,它就能使用簡單而又方便的符號來表示複雜的行為。EL表達式的格式是這樣的:用美元符號($)定界,內容包括在花括弧({})中,如清單 3 所示。
清單 3. 說明 EL表達式定界符的 JSTL 操作
此外,您可以將多個表達式與靜態文本組合在一起以通過字元串並置來構造動態屬性值,如清單 4 所示。單獨的表達式由標識符、存取器、文字和運算符組成。標識符用來引用存儲在數據中心中的數據對象。EL 有 11 個保留標識符,對應於 11 個 EL 隱式對象。假定所有其它標識符都引用 限制了作用域的變數。存取器用來檢索對象的特性或集合的元素。文字表示固定的值 ― 數字、字元、字元串、布爾型或空值。運算符允許對數據和文字進行組合以及比較。
清單 4. 組合靜態文本和多個 EL表達式以指定動態屬性值
限制了作用域的變數
JSP API 通過 操作允許從 JSP容器內的四個不同作用域中存儲和檢索數據。JSTL 通過提供用於指定和除去這些作用域中的對象的附加操作來擴展這一能力。此外,EL 提供將這些對象作為限制了作用域的變數進行檢索的內置支持。特別地,任何出現在 EL表達式中但不對應於任何 EL 隱式對象的標識符,都被自動假定為引用存儲在四個 JSP作用域的其中某個中的對象,這四個作用域是:
頁面作用域
請求作用域
會話作用域
應用程序作用域
您可能還記得,只有在為特定請求處理頁面期間才能檢索存儲在該頁面作用域中的對象。如果對象是存儲在請求作用域中的,可以在處理所有參與處理某請求的頁面期間檢索這些對象(譬如在對某個請求的處理中遇到了一個或多個 或 操作)。如果對象是存儲在會話作用域中的,則在與 Web應用程序的互動式會話期間,可以由用戶訪問的任何頁面檢索它(即,直到與該用戶交互相關聯的 HttpSession 對象無效為止)。可以由任何用戶從任何頁面訪問存儲在應用程序作用域中的對象,直到卸載 Web應用程序本身為止(通常是由於關閉 JSP容器所致)。
通過將字元串映射為期望作用域中的對象來將對象存儲到該作用域。然後,就可以通過提供相同字元串來從該作用域檢索該對象。在作用域的映射中查找字元串,並返回被映射的對象。在 Servlet API 中,將此類對象稱為相應作用域的 屬性。但是,在 EL 的上下文中,也將與屬性相關聯的字元串看作變數的名稱,該變數通過屬性映射的方式獲得特定的值。
在 EL 中,與隱式對象無關聯的標識符被認為是存儲在四個 JSP作用域中的名稱對象。首先對頁面作用域檢查是否存在這樣的標識符,其次對請求作用域、然後對會話作用域、最後對應用程序作用域依次進行這樣的檢查,然後測試該標識符的名稱是否與存儲在該作用域中的某個對象的名稱匹配。第一個這樣的匹配作為 EL標識符的值被返回。通過這種方法,可以將 EL標識符看作引用限制了作用域的變數。
從更技術的方面來說,沒有映射到隱式對象的標識符是用 PageContext 實例的 findAttribute() 方法求值的,該實例表示對頁面的處理,在該頁面上,當前正在處理用於請求的表達式。標識符的名稱作為參數傳遞給這個方法,然後該方法依次在四個作用域中搜索具有相同名稱的屬性。並將所找到的第一個匹配項作為 findAttribute() 方法的值返回。如果未在這四個作用域中找到這樣的屬性,則返回 null 。
最終,限制了作用域的變數是四個 JSP 作用域的屬性,這些屬性具有可以用作 EL標識符的名稱。只要對限制了作用域的變數賦予由字母數字組成的名稱,就可以通過 JSP 中提供的用於設置屬性的任何機制來創建它們。這包括內置的 操作,以及由 Servlet API 中的幾個類定義的 setAttribute() 方法。此外,四個 JSTL 庫中定義的許多定製標記本身就能夠設置作為限制了作用域的變數使用的屬性值。
表 1 中列出了 11 個 EL 隱式對象的標識符。不要將這些對象與 JSP 隱式對象(一共只有九個)混淆,其中只有一個對象是它們所共有的。
表 1. EL 隱式對象
類別標識符描述
JSP pageContext PageContext 實例對應於當前頁面的處理
作用域pageScope 與頁面作用域屬性的名稱和值相關聯的 Map 類
requestScope 與請求作用域屬性的名稱和值相關聯的 Map 類
sessionScope 與會話作用域屬性的名稱和值相關聯的 Map 類
applicationScope 與應用程序作用域屬性的名稱和值相關聯的 Map 類
請求參數 param 按名稱存儲請求參數的主要值的 Map 類
paramValues 將請求參數的所有值作為 String數組存儲的 Map 類
請求頭 header 按名稱存儲請求頭主要值的 Map 類
headerValues 將請求頭的所有值作為 String數組存儲的 Map 類
Cookie cookie 按名稱存儲請求附帶的 cookie 的 Map 類
初始化參數 initParam 按名稱存儲 Web應用程序上下文初始化參數的 Map 類
儘管 JSP 和 EL 隱式對象中只有一個公共對象( pageContext ),但通過 EL 也可以訪問其它 JSP 隱式對象。原因是 pageContext 擁有訪問所有其它八個 JSP 隱式對象的特性。實際上,這是將它包括在 EL 隱式對象中的主要理由。
其餘所有 EL 隱式對象都是映射,可以用來查找對應於名稱的對象。前四個映射表示先前討論的各種屬性作用域。可以用它們來查找特定作用域中的標識符,而不用依賴於 EL 在預設情況下使用的順序查找過程。
接下來的四個映射用來獲取請求參數和請求頭的值。因為 HTTP 協議允許請求參數和請求頭具有多個值,所以它們各有一對映射。每對中的第一個映射返回請求參數或頭的主要值,通常是恰巧在實際請求中首先指定的那個值。每對中第二個映射允許檢索參數或頭的所有值。這些映射中的鍵是參數或頭的名稱,但這些值是 String 對象的數組,其中的每個元素都是單一參數值或頭值。
cookie 隱式對象提供了對由請求設置的 cookie 名稱的訪問。這個對象將所有與請求相關聯的 cookie 名稱映射到表示那些 cookie 特性的 Cookie 對象。
最後一個 EL 隱式對象 initParam 是一個映射,它儲存與 Web 應用程序相關聯的所有上下文的初始化參數的名稱和值。初始化參數是通過web.xml部署描述符文件指定的,該文件位於應用程序的 WEB-INF 目錄中。
因為 EL標識符是作為隱式對象或限制了作用域的變數(通過屬性來實現)解析的,因此有必要將它們轉換成 Java 對象。EL 可以自動包裝和解包其相應的 Java 類中的基本類型(例如,可以在後台將 int強制轉換成 Integer 類,反之亦可),但大多數的標識符將成為指向完整的 Java 對象的指針。
結果是,對這些對象的特性或(在對象是數組和集合的情況下)對其元素的訪問通常是令人滿意的。就為了實現這種用途,EL 提供了兩種不同的存取器(點運算符( . )和方括弧運算符( [] )),也支持通過 EL 操作特性和元素。
點運算符通常用於訪問對象的特性。例如,在表達式${user.firstName} 中,使用點運算符來訪問 user標識符所引用對象的名為 firstName 的特性。EL 使用 Java bean 約定訪問對象特性,因此必須定義這個特性的 getter 方法(通常是名為 getFirstName() 的方法),以便表達式正確求值。當被訪問的特性本身是對象時,可以遞歸地應用點運算符。例如,如果我們虛構的 user 對象有一個實現為 Java 對象的 address 特性,那麼也可以用點運算符來訪問這個對象的特性。例如,表達式${user.address.city} 將會返回這個地址對象嵌套的 city 特性。
方括弧運算符用來檢索數組和集合的元素。在數組和有序集合(也即,實現了 java.util.List 介面的集合)的情況下,把要檢索的元素的下標放在方括弧中。例如,表達式${urls[3]} 返回 urls標識符所引用的數組或集合的第四個元素(和 Java 語言以及 JavaScript 中一樣,EL 中的下標是從零開始的)。
對於實現 java.util.Map 介面的集合,方括弧運算符使用關聯的鍵查找存儲在映射中的值。在方括弧中指定鍵,並將相應的值作為表達式的值返回。例如,表達式${commands["dir"]} 返回與 commands標識符所引用的 Map 中的 "dir" 鍵相關聯的值。
對於上述兩種情況,都可允許表達式出現在方括弧中。對嵌套表達式求值的結果將被作為下標或鍵,用來檢索集合或數組的適當元素。和點運算符一樣,方括弧運算符也可以遞歸應用。這使得 EL 能夠從多維數組、嵌套集合或兩者的任意組合中檢索元素。此外,點運算符和方括弧運算符還可以互操作。例如,如果數組的元素本身是對象,則可以使用方括弧運算符來檢索該數組的元素,並結合點運算符來檢索該元素的一個特性(例如 ${urls[3].protocol} )。
假定 EL 充當指定動態屬性值的簡化語言,EL 存取器有一個有趣的功能(與 Java 語言的存取器不同),那就是它們在應用於 null 時不拋出異常。如果應用 EL 存取器的對象(例如, ${foo.bar} 和 ${foo["bar"]} 中的 foo標識符)是 null ,那麼應用存取器的結果也是 null 。事實證明,在大多數情況下,這是一個相當有用的行為,不久您就會了解這一點。
最後,點運算符和方括弧運算符可能實現某種程度的互換。例如,也可以使用 ${user["firstName"]} 來檢索 user 對象的 firstName 特性,正如可以用 ${commands.dir} 獲取與 commands 映射中的 "dir" 鍵相關聯的值一樣。
以下是el表達式對javabean組件、數組,Map集合、List集合中數據調用方法的簡要總結:
數據類型 | 示例用法 | 實際調用方法 |
JavaBean組件 | ${colorBean.red} ${colorBean["red"]} | colorBean.getRed() |
數組 | ${colorArray[2]} ${colorArray["2"]} | Array.get(colorArray, 2) |
List | colorList[2] colorList["2"] | colorList.get(2) |
Map | colorMap[red] colorMap["red"] | colorMap.get(pageContext.findAttribute("red")) colorMap.get("red") |
EL 還可以通過使用標識符和存取器,遍歷包含應用程序數據(通過限制了作用域的變數公開)或關於環境的信息(通過 EL 隱式對象)的對象層次結構。但是,只是訪問這些數據,通常不足以實現許多 JSP應用程序所需的表示邏輯。
最終,EL 還包括了幾個用來操作和比較 EL表達式所訪問數據的運算符。表 2 中匯總了這些運算符。
表 2. EL 運算符
類別運算符
算術運算符 + 、 - 、 * 、 / (或 div )和 % (或 mod )
關係運算符== (或 eq )、 != (或 ne )、 < (或 lt )、 > (或 gt )、 <= (或 le )和 >= (或 ge )
邏輯運算符 && (或 and )、 || (或 or )和 ! (或 not )
驗證運算符 empty
算術運算符支持數值的加法、減法、乘法和除法。還提供了一個求余運算符。註:除法和求余運算符都有替代的、非符號的名稱(為的是與 XPath 保持一致)。清單 5 中顯示了一個演示算術運算符用法的示例表達式。對幾個 EL表達式應用算術運算符的結果是將該算術運算符應用於這些表達式返回的數值所得的結果。
清單 5. 利用算術運算符的 EL 表達式
${item.price * (1 + taxRate[user.address.zipcode])}
關係運算符允許比較數字或文本數據。比較的結果作為布爾值返回。邏輯運算符允許合併布爾值,返回新的布爾值。因此,可以將 EL邏輯運算符應用於嵌套的關係或邏輯運算符的結果,如清單 6 所示。
清單 6. 利用關係和邏輯運算符的 EL 表達式
${(x >= min) && (x <= max)}
最後一種 EL運算符是 empty ,它對於驗證數據特別有用。 empty運算符採用單個表達式作為其變數(也即, ${empty input} ),並返回一個布爾值,該布爾值表示對表達式求值的結果是不是“空”值。求值結果為 null 的表達式被認為是空,即無元素的集合或數組。如果參數是對長度為零的 String 求值所得的結果,則 empty運算符也將返回 true 。
表 3 顯示了 EL 運算符的優先順序。正如清單 5 和 6 所示,可以用圓括弧對表達式分組,高於普通的優先順序規則。
表 3. EL運算符優先順序(自頂到底,從左到右)
[] , .
()
unary - 、 not 、 ! 、 empty
* 、 / 、 div 、 % 、 mod
+ 、binary -
() < 、 > 、 <= 、 >= 、 lt 、 gt 、 le 、 ge
== 、 != 、 eq 、 ne
&& 、 and
|| 、 or
在 EL表達式中,數字、字元串、布爾值和 null 都可以被指定為文字值。字元串可以用單引號或雙引號定界。
布爾值被指定為 true 和 false 。
Taglib偽指令
正如我們先前討論的,JSTL 1.0 包括四個定製標記庫。為了演示 JSTL 標記和表達式語言的交互,我們將研究幾個來自 JSTL core 庫的標記。和使用任何 JSP 定製標記庫一樣,必須在您想要使用這個庫標記的任何頁面中包括 taglib 偽指令。清單 7 顯示了用於這個特定庫的偽指令。
清單 7. 用於 JSTL core 庫 EL 版本的 taglib 偽指令
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
實際上,對應於 JSTL core 庫的 taglib 偽指令有兩種,因為在 JSTL 1.0 中,EL 是可選的。所有四個 JSTL 1.0 定製標記庫都有使用 JSP表達式(而不是 EL)指定動態屬性值的備用版本。因為這些備用庫依賴於 JSP 的更傳統的請求時屬性值,所以它們被稱為 RT庫,而那些使用表達式語言的則被稱為 EL 庫。開發人員用不同的 taglib 偽指令來區分每個庫的這兩個版本。清單 8 顯示了使用 core 庫的 RT 版本的偽指令。但是,由於我們討論的重點是 EL,所以首先需要這些偽指令。
清單 8. 用於 JSTL core 庫 RT 版本的 taglib 偽指令
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c_rt" %>
我們首先要考慮的 JSTL 定製標記是 操作。正如已經說明的,限制了作用域的變數在 JSTL 中起關鍵作用, 操作提供基於標記的機制來創建和設置限制了作用域的變數。清單 9 中顯示了該操作的語法,其中 var 屬性指定了限制了作用域的變數的名稱, scope 屬性表明了該變數駐留在哪個作用域中, value 屬性指定了分配給該變數的值。如果指定變數已經存在,則簡單地將所指明的值賦給它。如果不存在,則創建新的限制了作用域的變數,並用該值初始化這個變數。
清單 9. 操作的語法
scope="scope"
value="expression"/>
scope 屬性是可選的,其預設值是 page 。
清單 10 中顯示了 的兩個示例。在第一個示例中,將會話作用域變數設置成 String 值。在第二個示例中,用表達式來設置數值:將頁面作用域內名為 square 的變數賦值為名為 x 的請求參數的值的平方。
清單 10. 操作示例
您還可以將限制了作用域的變數的值指定為 操作的主體內容,而不是使用屬性。使用這種方法,您可以重新編寫清單 10 中的第一個示例,如清單 11 所示。此外,正如我們馬上可以看到的, 標記的主體內容本身也可以使用定製標記。 主體內生成的所有內容都將作為一個 String 值賦給指定變數。
清單 11. 通過主體內容指定 操作的值
CST
JSTL core 庫包含第二個用於管理限制了作用域的變數的標記 ― 。顧名思義, 操作是用來刪除限制了作用域的變數的,它獲取兩個屬性。 var 屬性指定待刪除變數的名稱, scope 屬性是可選的,它表示待刪除變數來自哪個作用域,預設為 page ,如清單 12 所示。
清單 12. 操作示例
儘管 操作允許將表達式結果賦給限制了作用域的變數,但開發人員通常會希望只顯示錶達式的值,而不存儲它。JSTL 定製標記承擔這一任務,其語法如清單 13 所示。該標記對由其 value 屬性指定的表達式進行求值,然後列印結果。如果指定了可選屬性 default ,那麼,在對 value 屬性的表達式求值所得結果為 null 或空 String 的情況下, 將列印其值。
清單 13. 操作的語法
default="expression"
escapeXml="boolean"/>
escapeXml 屬性也是可選的。它控制當用
標記輸出諸如“<”、“>”和“&”之類的字元(在 HTML 和 XML 中具有特殊意義)時是否應該進行轉義。如果將 escapeXml 設置為 true,則會自動將這些字元轉換成相應的 XML 實體(此處提到的字元分別轉換成 < 、 > 和 & )。例如,假定有一個名為 user 的會話作用域變數,它是一個類的實例,該類為用戶定義了兩個特性: username 和 company 。每當用戶訪問站點時,這個對象被自動分配給會話,但直到用戶實際登錄后,才會設置這兩個特性。假定是這種方案,請考慮清單 14 中的 JSP 片段。在用戶登錄之後,這個片段將顯示單詞“Hello”,其後是他/她的用戶名和一個驚嘆號。但是,在用戶登錄之前,由這個片段生成的內容則是短語“Hello Guest!”。在這種情況下,因為 username 特性還有待初始化,所以 標記將轉而列印出 default 屬性的值(即字元串“Guest”)。
清單 14. 帶預設內容的 操作示例
Hello !
接下來,考慮清單 15,它使用了 標記的 escapeXml 屬性。如果在這種情況下已經將 company 特性設置成 Java String 值 "Flynn & Sons" ,那麼,實際上該操作生成的內容將是 Flynn & Sons 。如果這個操作是生成 HTML 或 XML 內容的 JSP 頁面的一部分,那麼,這個字元串中間的“&”符號最終可能被解釋為 HTML 或 XML控制字元,從而妨礙了對該內容的顯示或解析。但是,如果將 escapeXml 屬性值設置成 true ,則所生成的內容將是 Flynn & Sons 。瀏覽器或解析器不會因在解釋時遇到這種內容而出問題。假定 HTML 和 XML 是 JSP應用程序中最常見的內容類型,所以 escapeXml 屬性的預設值是 true 就不足為奇了。
清單 15. 禁用轉義的 操作示例
用預設值設置變數
除了簡化動態數據的顯示之外,當通過 設置變數值時, 指定預設值的能力也很有用。正如 清單 11 所示,用來賦給限制了作用域的變數的值可以指定為 標記的主體內容,也可以通過其值屬性來指定。通過將 操作嵌套在 標記的主體內容中,變數賦值就可以利用其預設值能力。
清單 16 中說明了這種方法。外部 標記的行為非常簡單:它根據其主體內容設置會話作用域timezone變數的值。但是,在這種情況下,主體內容是通過 操作生成的。這個嵌套操作的值屬性是表達式${cookie['tzPref'].value} ,它嘗試通過 cookie 隱式對象返回名為 tzPref 的 cookie 值。( cookie 隱式對象將 cookie 名稱映射到相應的 Cookie 實例,這意味著必須通過對象的 value 特性使用點運算符來檢索儲存在 cookie 中的實際數據。)
清單 16. 合併 和 以提供預設變數值
但是,請考慮以下情況,用戶是第一次嘗試使用這段代碼的 Web 應用程序。結果是,請求中沒有提供名為 tzPref 的 cookie。這意味著使用隱式對象的查找將返回 null ,在這種情況下整個表達式將返回 null 。因為對 標記的 value 屬性求值的結果是 null ,所以 標記會轉而輸出對其 default 屬性求值的結果。在這裡是字元串 CST 。因此,實際的結果是將 timezone 限制了作用域的變數設置成用戶的 tzPref cookie 中存儲的時區,或者,如果沒有,則使用預設時區 CST 。
EL 和 JSP 2.0
表達式語言僅可用於指定 JSTL 定製標記中的動態屬性值。但 JSTL 1.0表達式語言的一個擴展已經被提出,會把它包括到 JSP 2.0 中去,眼下正在進行最後評審。這個擴展將允許開發人員通過自己的定製標記來使用 EL。頁面作者將可以在當前允許使用 JSP表達式的任何地方使用 EL表達式,譬如將動態值插入模板文本中:
Your preferred time zone is $
。
這個 JSP 2.0 功能(就象 JSTL 本身一樣)將支持頁面作者進一步減少對 JSP 編製腳本元素的依賴,從而改進 JSP應用程序的可維護性。
fn:contains(string, substring)
如果參數string中包含參數substring,返回true
fn:containsIgnoreCase(string, substring)
如果參數string中包含參數substring(忽略大小寫),返回true
fn:endsWith(string, suffix)
如果參數 string 以參數suffix結尾,返回true
fn:escapeXml(string)
將有特殊意義的XML (和HTML)轉換為對應的XML實體字元,並返迴轉義后的字元
fn:indexOf(string, substring)
返回參數substring在參數string中第一次出現的位置
fn:join(array, separator)
將一個給定的數組array用給定的間隔符separator串在一起,組成一個新的字元串並返回。
fn:length(item)
返回參數item中包含元素的數量。
參數Item類型是普通對象、數組、Collection、Map、Iterator迭代器、Enumeration枚舉對象、
或者String。
如果是String類型,返回值是String中的字元數。
如果是數組類型,返回值是數組的長度。
如果是Collection容器類的子類,返回值是該容器類的包含元素的個數。
如果是
Map類型,返回值是此映射中的鍵-值映射關係數。
如果是Iterator類型,返回值是Iterator中的元素個數。
如果是Enumeration類型,返回值是Enumeration中的元素個數
fn:replace(string, before, after)
返回一個String對象。
用參數after字元串替換參數string中所有出現參數before字元串的地方,並返回替換后的結果
fn:split(string, separator)
返回一個數組,以參數separator 為分割符分割參數string,分割后的每一部分就是數組的一個元素
fn:startsWith(string, prefix)
如果參數string以參數prefix開頭,返回true
fn:substring(string, begin, end)
返回參數string部分字元串, 從參數begin開始到參數end位置,包括end位置的字元
fn:substringAfter(string, substring)
返回參數substring在參數string中後面的那一部分字元串
fn:substringBefore(string, substring)
返回參數substring在參數string中前面的那一部分字元串
fn:toLowerCase(string)
將參數string所有的字元變為小寫,並將其返回
fn:toUpperCase(string)
將參數string所有的字元變為大寫,並將其返回
fn:trim(string)
去除參數string 首尾的空格,並將其返回
EL(與四個 JSTL 定製標記庫提供的操作結合起來)允許頁面作者不使用腳本元素即可實現表示層邏輯。例如,對比本文開頭 清單 1 中的 JSP代碼和清單 17 中顯示的通過 JSTL 實現的同樣功能。(JSTL core 庫中其餘的標記,包括 及其子標記,將在本系列的下一篇文章中討論。)儘管顯然執行了條件邏輯,但是 JSTL 版本中沒有 Java 語言源代碼,並且標記之間的關係(尤其是關於嵌套需求)對於任何精通 HTML 語法的人都應該是熟悉的。
清單 17. 合併 和 以提供預設變數值
通過提供大多數 Web應用程序常用功能的標準實現,JSTL 有助於加速開發周期。與 EL 結合起來,JSTL 可以不需要對錶示層程序編寫代碼,這極大地簡化了 JSP應用程序的維護。
Date | Item |
2009/04/22 | Moved to a Maven based build system. |
2008/12/21 | A patch from Robert Goff has moved the trunk of the Standard Taglib up towards JSTL 1.2 level. |
10/25/2004 | Standard Taglib version 1.1.2 - A minor bug fix update - is now available. |
07/20/2004 | Standard Taglib version 1.1.1 released - A minor bug fix update - is now available. |
01/30/2004 | Standard Taglib version 1.1.0 - First official release of our implementation of JSTL 1.1 - is now available. |
09/25/2003 | Standard Taglib version 1.1.0-B1 - early access (Beta 1) of our implementation of JSTL 1.1 - is now available. |
參考資料:JSTL官方網站 |