天天看點

jstl标簽庫基礎教程及其使用代碼

原創整理不易,轉載請注明出處:

代碼下載下傳位址:

在 JSP 頁面中,使用标簽庫代替傳統的 Java 片段語言來實作頁面的顯示邏輯已經不是新技術了,然而,由自定義标簽很容易造成重複定義和非标準的實作。鑒于此,出現了 JSTL ( JSP Standard Tag Library )。大多數 JSP 頁面邏輯提供了實作的 JSTL 技術,該技術本身就是一個标簽庫。

Sun 公司 Java 規範标準的 JSTL 由 apache jakarta 組織負責維護。作為開源的标準技術,它一直在不斷地完善。 JSTL 的釋出包有兩個版本: Standard-1.0 Taglib 、 Standard-1.1 Taglib ,它們在使用時是不同的。

         Standard-1.0 Taglib ( JSTL1.0 )支援 Servlet2.3 和 JSP1.2 規範, Web 應用伺服器 Tomcat4 支援這些規範,而它的釋出也在 Tomcat 4.1.24 測試通過了。

         Standard-1.1 Taglib ( JSTL1.1 )支援 Servlet2.4 和 JSP2.0 規範, Web 應用伺服器 Tomcat5 支援這些規範,它的釋出在 Tomcat 5.0.3 測試通過了。

在本章的介紹中,将以由 Sun 釋出的 Standard-1.1 Taglib 标簽庫為主,而 apache jakarta 組織釋出的開源标簽庫,可以從 http://jakarta.apache.org/taglibs/ 找到所需要的幫助。 Sun 釋出的标準 JSTL1.1 标簽庫有以下幾個标簽:

         核心标簽庫:包含 Web 應用的常見工作,比如:循環、表達式指派、基本輸入輸出等。

         國際化标簽庫:用來格式化顯示資料的工作,比如:對不同區域的日期格式化等。

         資料庫标簽庫:可以做通路資料庫的工作。

         XML 标簽庫:用來通路 XML 檔案的工作,這是 JSTL 标簽庫的一個特點。

         函數标簽庫:用來讀取已經定義的某個函數。

此外, JSTL 還提供了 EL 表達式語言( Expression Language )來進行輔助的工作。

JSTL 标簽 庫由标簽庫和 EL 表達式語言兩個部分組成。 EL 在 JSTL 1.0 規範中被引入,當時用來作為 Java 表達式來工作,而該表達式必須配合 JSTL 的标簽庫才能得到需要的結果。

說明:在 JSTL 1.1 規範中, JSP2.0 容器已經能夠獨立的了解任何 EL 表達式。 EL 可以獨立出現在 JSP 頁面的任何角落。本文随後的内容将以 JSTL 1.1 規範作為介紹的重點。

EL 是從 JavaScript 腳本語言得到啟發的一種表達式語言,它借鑒了 JavaScript 多類型轉換無關性的特點。在使用 EL 從 scope 中得到參數時可以自動轉換類型,是以對于類型的限制更加寬松。 Web 伺服器對于 request 請求參數通常會以 String 類型來發送,在得到時使用的 Java 語言腳本就應該是 request.getParameter(“XXX”) ,這樣的話,對于實際應用還必須進行強制類型轉換。而 EL 就将使用者從這種類型轉換的繁瑣工作脫離出來,允許使用者直接使用

EL 表達式取得的值,而不用關心它是什麼類型。

下面的示例就是一個 EL 表達式,見例 9.1 。

例 9.1 :簡單 EL 表達式

這個示例将在 JSP 頁面顯示為“ 1 ”, EL 表達式必須以“ ${XXX} ”來表示,其中“ XXX ”部分就是具體表達式内容,“ ${} ”将這個表達式内容包含在其中作為 EL 表達式的定義。本示例可以在滿足 JSP2.0 規範的任何 Web 應用伺服器中使用。

一個 EL 表達式包含變量和操作符兩個内容。任何存在于 JSP 作用範圍的 JavaBean 都可以被轉化成 EL 表達式來使用,它所包含的預設變量如下:

1 .預設變量 pageScope 、 requestScope 、 sessionScope 、 applicationScope

這 4 個預設變量包含 Scope 作用範圍的參數集合,相當于被儲存在 java.util.Map 中的某個參數。下面看簡單的示例 9.2 :

例 9.2 :使用 sessionScope 變量的 EL 表達式

<%request.getSession().setAttribute("sampleValue", new Integer(10));%>

${sessionScope.sampleValue}

取得儲存在 Session 中參數的 sessionScope 變量的 EL 表達式,“ . ”是 property 通路操作符,在這裡表示從 Session 中取得“鍵”為“ sampleValue ”的參數,并顯示出來。顯示結果為“ 10 ”。

2 .預設變量 param 、 paramValues

這兩個預設變量包含請求參數的集合, param 表明請求包含的參數為單一控件, paramValues 表明請求包含的參數為控件數組。下面看一個簡單示例 9.3 :

例 9.3 :送出請求的頁面和接受的頁面

在這個頁面中定義了兩組控件,控件名為“ sampleValue ”的是一套控件數組,控件名為“ sampleSingleValue ”的是單一控件,通過遞交将請求參數傳送到 SampleJsp.jsp 。

這是請求轉發到的頁面,通過 EL 表達式的 paramValues 變量得到控件數組中最後一個控件的遞交參數,通過 EL 表達式的 param 變量得到單一控件的遞交參數。控件數組參數的 EL 表達式使用“ [] ”來指定數組下标。本示例将顯示控件數組中最後一個控件的值“ 12 ”和單一控件的值“ SingleValue ”。

3 .預設變量 header 、 headerValues

這兩個預設變量包含請求參數頭部資訊的集合, header 變量表示單一頭部資訊, headerValues 則表示數組型的頭部資訊。

4 .預設變量 cookie

包含所有請求的 cookie 集合,集合中的每個對象對應 javax.servlet.http.Cookie 。

5 .預設變量 initParam

包含所有應用程式初始化參數的集合。

6 .預設變量 pageContext

等價于 page 環境類 javax.servlet.jsp.PageContext 的執行個體,用來提供通路不同的請求參數。

11 個預設變量幾乎包含了 Web 應用的所有基本操作,若一個表達式不使用這些變量而直接使用參數名,那麼就采用就近原則。該表達式将使用最近取得的參數值。

9.2.3  EL 表達式的操作符

EL 表達式中還有許多操作符可以幫助完成各種所需的操作,之前的示例中“ . ”、“ [] ”就是其中的兩個,下面将用表 9.1 來展示所有操作符及它們各自的功能。

表 9.1  EL 表達式的操作符

操作符

功能和作用

.

通路一個 bean 屬性或者 Map entry

[]

通路一個數組或者連結清單元素

()

對子表達式分組,用來改變指派順序

? :

條件語句,比如:條件 ?ifTrue:ifFalse

如果條件為真,表達式值為前者,反之為後者

+

數學運算符,加操作

-

數學運算符,減操作或者對一個值取反

*

數學運算符,乘操作

/ 或 div

數學運算符,除操作

% 或 mod

數學運算符,模操作 ( 取餘 )

== 或 eq

邏輯運算符,判斷符号左右兩端是否相等,如果相等傳回 true ,否則傳回 false

!= 或 ne

邏輯運算符,判斷符号左右兩端是否不相等,如果不相等傳回 true ,否則傳回 false

< 或 lt

邏輯運算符,判斷符号左邊是否小于右邊,如果小于傳回 true ,否則傳回 false

> 或 gt

邏輯運算符,判斷符号左邊是否大于右邊,如果大于傳回 true ,否則傳回 false

<= 或 le

邏輯運算符,判斷符号左邊是否小于或者等于右邊,如果小于或者等于傳回 true ,否則傳回 false

>= 或 ge

邏輯運算符,判斷符号左邊是否大于或者等于右邊,如果大于或者等于傳回 true ,否則傳回 false

&& 或 and

邏輯運算符,與操作賦。如果左右兩邊同為 true 傳回 true ,否則傳回 false

|| 或 or

邏輯運算符,或操作賦。如果左右兩邊有任何一邊為 true 傳回 true ,否則傳回 false

! 或 not

邏輯運算符,非操作賦。如果對 true 取運算傳回 false ,否則傳回 true

empty

用來對一個空變量值進行判斷 : null 、一個空 String 、空數組、 空 Map 、沒有條目的 Collection 集合

func(args)

調用方法 , func 是方法名, args 是參數,可以沒有,或者有一個、多個參數 . 參數間用逗号隔開

這些操作符都是極其有用的,下面通過幾個示例來示範它們的使用方法:

例 9.4 :幾組操作符的示例

${pageScope.sampleValue + 12} <br>            // 顯示 12

${(pageScope.sampleValue + 12)/3} <br>      // 顯示 4.0

${(pageScope.sampleValue + 12) /3==4} <br>         // 顯示 true

${(pageScope.sampleValue + 12) /3>=5} <br>         // 顯示 false

<input type="text" name="sample1" value="${pageScope.sampleValue + 10}"> // 顯示值為 10 的 Text 控件

可以看到,對于這些示例,程式設計者完全無需管理它們的類型轉換,在表達式内部都已經處理了。有了 EL 表達式,在 JSP 頁面的程式設計變得更靈活,也更容易。

在 JSTL1.1 中有以下這些标簽庫是被支援的: Core 标簽庫、 XML processing 标簽庫、 I18N formatting 标簽庫、 Database access 标簽庫、 Functions 标簽庫。對應的辨別符見表 9.2 所示:

表 9.2 标簽庫的辨別符

标簽庫

URI

字首

Core

http://java.sun.com/jsp/jstl/core

c

XML processing

http://java.sun.com/jsp/jstl/xml

x

I18N formatting

http://java.sun.com/jsp/jstl/fmt

fmt

Database access

http://java.sun.com/jsp/jstl/sql

sql

Functions

http://java.sun.com/jsp/jstl/functions

fn

下面看例 9.5 ,簡單使用标簽庫的示例。

例 9.5 :簡單 JSTL 标簽庫示例

在該示例的 JSP 頁面中聲明了将使用 Core 标簽庫,它的 URI 為“ http://java.sun.com/jsp/jstl/core ”,字首為“ c ”。之後,頁面中 <c:forEach> 标簽就是使用了 JSTL 的标簽進行了工作。對于該标簽的功能,這裡暫時不作具體講解,隻是讓讀者能夠有個簡單的概念,了解怎樣定義和使用标簽庫。

Core 标簽庫,又被稱為核心标簽庫,該标簽庫的工作是對于 JSP 頁面一般處理的封裝。在該标簽庫中的标簽一共有 14 個,被分為了四類,分别是:

多用途核心标簽: <c:out> 、 <c:set> 、 <c:remove> 、 <c:catch> 。

條件控制标簽: <c:if> 、 <c:choose> 、 <c:when> 、 <c:otherwise> 。

循環控制标簽: <c:forEach> 、 <c:forTokens> 。

URL 相關标簽: <c:import> 、 <c:url> 、 <c:redirect> 、 <c:param> 。

以下是各個标簽的用途和屬性以及簡單示例。

<c:out> 标簽是一個最常用的标簽,用于在 JSP 中顯示資料。它的屬性和描述如表 9.3 所示:

表 9.3   <c:out> 标簽屬性和說明

屬性

描述

value

輸出到頁面的資料,可以是 EL 表達式或常量(必須)

default

當 value 為 null 時顯示的資料(可選)

escapeXml

當設定為 true 時會主動更換特殊字元,比如“ <,>,& ”(可選,預設為 true )

在 JSTL1.0 的時候,在頁面顯示資料必須使用 <c:out> 來進行。然而,在 JSTL1.1 中,由于 JSP2.0 規範已經預設支援了 EL 表達式 ,是以可以直接在 JSP 頁面使用表達式。下面看一個示例。

<c:out value="${sessionScope.anyValue}" default="no value" escapeXml="false"/>

該示例将從 Session 查找名為“ anyValue ”的參數,并顯示在頁面,若沒有找到則顯示“ no value ”。

<c:set> 标簽用于為變量或 JavaBean 中的變量屬性指派的工作。它的屬性和描述如表 9.4 所示:

表 9.4   <c:set> 标簽屬性和說明

值的資訊,可以是 EL 表達式或常量

target

被指派的 JavaBean 執行個體的名稱,若存在該屬性則必須存在 property 屬性(可選)

property

JavaBean 執行個體的變量屬性名稱(可選)

var

被指派的變量名(可選)

scope

變量的作用範圍,若沒有指定,預設為 page (可選)

當不存在 value 的屬性時,将以包含在标簽内的實體資料作為指派的内容。下面看一個示例:

<c:set value="this is andy" var="oneString"/>

${oneString} <br>

該示例将為名為“ oneString ”的變量指派為“ this is andy ”,其作用範圍為 page 。

<c:remove> 标簽用于删除存在于 scope 中的變量。它的屬性和描述如表 9.5 所示:

表 9.5   <c:remove> 标簽屬性和說明

需要被删除的變量名

變量的作用範圍,若沒有指定,預設為全部查找(可選)

下面看一個示例:

<c:remove var="sampleValue" scope="session"/>

${sessionScope.sampleValue} <br>

該示例将存在于 Session 中名為“ sampleValue ”的變量删除。下一句 EL 表達式顯示該變量時,該變量已經不存在了。

<c:catch> 标簽允許在 JSP 頁面中捕捉異常。它包含一個 var 屬性,是一個描述異常的變量,改變量可選。若沒有 var 屬性的定義,那麼僅僅捕捉異常而不做任何事情,若定義了 var 屬性,則可以利用 var 所定義的異常變量進行判斷轉發到其他頁面或提示報錯資訊。看一個示例。

<c:catch var="err">

         ${param.sampleSingleValue[9] == 3}

</c:catch>

${err}

當“ ${param.sampleSingleValue[9] == 3} ”表達式有異常時,可以從 var 屬性“ err ”得到異常的内容,通常判斷“ err ”是否為 null 來決定錯誤資訊的提示。

<c:if> 标簽用于簡單的條件語句。它的屬性和描述如表 9.6 所示:

表 9.6   <c:if> 标簽屬性和說明

test

需要判斷的條件

儲存判斷結果 true 或 false 的變量名,該變量可供之後的工作使用(可選)

變量的作用範圍,若沒有指定,預設為儲存于 page 範圍中的變量(可選)

<c:if test="${paramValues.sampleValue[2] == 12}" var="visits">

      It is 12

</c:if><br>

${visits} <br>

該示例将判斷 request 請求送出的傳入控件數組參數中,下标為“ 2 ”的控件内容是否為“ 12 ”,若為 12 則顯示“ It is 12 ”。判斷結果被儲存在 page 範圍中的“ visits ”變量中。  

這三個标簽用于實作複雜條件判斷語句,類似“ if,elseif ”的條件語句。

<c:choose> 标簽沒有屬性,可以被認為是父标簽, <c:when> 、 <c:otherwise> 将作為其子标簽來使用。

<c:when> 标簽等價于“ if ”語句,它包含一個 test 屬性,該屬性表示需要判斷的條件。

<c:otherwise> 标簽沒有屬性,它等價于“ else ”語句。

下面看一個複雜條件語句的示例。

該示例将判斷 request 請求送出的傳入控件數組參數中,下标為“ 2 ”控件内容是否為“ 11 ”或“ 12 ”或“ 13 ”,并根據判斷結果顯示各自的語句,若都不是則顯示“ not 11 、 12 、 13 ”。

9.3.7  用于循環的 <c:forEach> 标簽

<c:forEach> 為循環控制标簽。它的屬性和描述如表 9.7 所示:

表 9.7   <c:forEach> 标簽屬性和說明

items

進行循環的集合(可選)

begin

開始條件(可選)

end

結束條件(可選)

step

循環的步長,預設為 1 (可選)

做循環的對象變量名,若存在 items 屬性,則表示循環集合中對象的變量名(可選)

varStatus

顯示循環狀态的變量(可選)

下面看一個集合循環的示例。

該示例将儲存在 Session 中的名為“ arrayList ”的 ArrayList 類型集合參數中的對象依次讀取出來, items 屬性指向了 ArrayList 類型集合參數, var 屬性定義了一個新的變量來接收集合中的對象。最後直接通過 EL 表達式顯示在頁面上。下面看一個簡單循環的示例。

<c:forEach var="i" begin="1" end="10" step="1">

      ${i}<br />

</c:forEach>

該示例從“ 1 ”循環到“ 10 ”,并将循環中變量“ i ”顯示在頁面上。

<c:forTokens> 标簽可以根據某個分隔符分隔指定字元串,相當于 java.util.StringTokenizer 類。它的屬性和描述如表 9.8 所示:

表 9.8   <c:forTokens> 标簽 屬性和說明

進行分隔的 EL 表達式或常量

delims

分隔符

做循環的對象變量名(可選)

下面看一個示例。

<c:forTokens items="aa,bb,cc,dd" begin="0" end="2" step="2" delims="," var="aValue">

         ${aValue}

</c:forTokens>

需要分隔的字元串為“ aa,bb,cc,dd ”,分隔符為“ , ”。 begin 屬性 指定從第一個“ , ”開始分隔, end 屬性指定分隔到第三個“ , ”,并将做循環的變量名指定為“ aValue ”。由于步長為“ 2 ”,使用 EL 表達式 ${aValue} 隻能顯示“ aa

<c:import> 标簽允許包含另一個 JSP 頁面到本頁面來。它的屬性和描述如表 9.9 所示:

表 9.9   <c:import> 标簽屬性和說明

url

需要導入頁面的 URL

context

Web Context 該屬性用于在不同的 Context 下導入頁面,當出現 context 屬性時,必須以“ / ”開頭,此時也需要 url 屬性以“ / ”開頭(可選)

charEncoding

導入頁面的字元集(可選)

可以定義導入文本的變量名(可選)

導入文本的變量名作用範圍(可選)

varReader

接受文本的 java.io.Reader 類變量名(可選)

<c:import url="/MyHtml.html" var="thisPage" />

<c:import url="/MyHtml.html" context=”/sample2” var="thisPage"/>

<c:import url="www.sample.com/MyHtml.html" var="thisPage"/>

該示例示範了三種不同的導入方法,第一種是在同一 Context 下的導入,第二種是在不同的 Context 下導入,第三種是導入任意一個 URL 。

<c:url> 标簽用于得到一個 URL 位址。它的屬性和描述如表 9.10 所示:

表 9.10  <c:url> 标簽屬性和說明

頁面的 URL 位址

Web Context 該屬性用于得到不同 Context 下的 URL 位址,當出現 context 屬性時,必須以“ / ”開頭,此時也需要 url 屬性以“ / ”開頭(可選)

URL 的字元集(可選)

存儲 URL 的變量名(可選)

變量名作用範圍(可選)

<c:url value="/MyHtml.html" var="urlPage" />

<a href="${urlPage}">link</a>

得到了一個 URL 後,以 EL 表達式放入 <a> 标簽的 href 屬性,達到連結的目的。

<c:redirect> 用于頁面的重定向,該标簽的作用相當于 response.setRedirect 方法的工作。它包含 url 和 context 兩個屬性,屬性含義和 <C:url> 标簽相同。下面看一個示例。

<c:redirect url="/MyHtml.html"/>

該示例若出現在 JSP 中,則将重定向到目前 Web Context 下的“ MyHtml.html ”頁面,一般會與 <c:if> 等标簽一起使用。

<c:param> 用來為包含或重定向的頁面傳遞參數。它的屬性和描述如表 9.11 所示:

表 9.11   <c:param> 标簽屬性和說明

name

傳遞的參數名

傳遞的參數值(可選)

下面是一個示例:

<c:redirect url="/MyHtml.jsp">

<c:param name="userName" value=”RW” />

</c:redirect>

該示例将為重定向的“ MyHtml.jsp ”傳遞指定參數“ userName=’RW’ ”。

在企業級應用越來越依賴 XML 的今天, XML 格式的資料被作為資訊交換的優先選擇。 XML processing 标簽庫為程式設計者提供了基本的對 XML 格式檔案的操作。在該标簽庫中的标簽一共有 10 個,被分為了三類,分别是:

q         XML 核心标簽: <x:parse> 、 <x:out> 、 <x:set> 。

q         XML 流控制标簽: <x:if> 、 <x:choose> 、 <x:when> 、 <x:otherwise> 、 <x:forEach> 。

q         XML 轉換标簽: <x:transform> 、 <x:param> 。

由于該組标簽庫專注于對某一特定領域的實作,是以本書将隻選擇其中常見的一些标簽和屬性進行介紹。

<x:parse> 标簽是該組标簽庫的核心,從其标簽名就可以知道,它是作為解析 XML 檔案而存在的。它的屬性和描述如表 9.12 所示:

表 9.12   <x:parse> 标簽屬性和說明

doc

源 XML 的内容,該屬性的内容應該為 String 類型或者 java.io.Reader 的執行個體,可以用 xml 屬性來替代,但是不被推薦

将解析後的 XML 儲存在該屬性所指定的變量中,之後 XML processing 标簽庫中的其他标簽若要取 XML 中的内容就可以從該變量中得到(可選)

變量的作用範圍(可選)

varDom

指定儲存的變量為 org.w3c.dom.Document 接口類型(可選)

scopeDom

org.w3c.dom.Document 的接口類型變量作用範圍(可選)

systemId

定義一個 URI ,該 URI 将被使用到 XML 檔案中以接入其他資源檔案(可選)

filter

該屬性必須為 org.xml.sax.XMLFilter 類的一個執行個體,可以使用 EL 表達式傳入,将對 XML 檔案做過濾得到自身需要的部分(可選)

其中, var 、 scope 和 varDom 、 scopeDom 不應該同時出現,而應該被視為兩個版本來使用,二者的變量都可以被 XML processing 标簽庫的其他标簽來使用。

<x:parse> 标簽單獨使用的情況很少,一般會結合 XML processing 标簽庫中的其他标簽來一起工作。下面看一個示例。

首先給出一個簡單的 XML 檔案,将對該 XML 檔案做解析,該 XML 檔案名為 SampleXml.xml 。

标簽庫的工作:

<c:import var="xmlFile" url="http://localhost:8080/booksamplejstl/SampleXml.xml"/>

<x:parse var="xmlFileValue" doc="${xmlFile}"/>

看到 I18N 就應該想到知識“國際化”, I18N formatting 标簽庫就是用于在 JSP 頁面中做國際化的動作。在該标簽庫中的标簽一共有 12 個,被分為了兩類,分别是:

         國際化核心标簽: <fmt:setLocale> 、 <fmt:bundle> 、 <fmt:setBundle> 、 <fmt:message> 、 <fmt:param> 、 <fmt:requestEncoding> 。

         格式化标簽: <fmt:timeZone> 、 <fmt:setTimeZone> 、 <fmt:formatNumber> 、 <fmt:parseNumber> 、 <fmt:formatDate> 、 <fmt:parseDate> 。

下面隻選擇其中常見的一些标簽和屬性進行介紹。

<fmt:setLocale> 标簽用于設定 Locale 環境。它的屬性和描述如表 9.17 所示:

表 9.17   <fmt:setLocale> 标簽屬性和說明

Locale 環境的指定,可以是 java.util.Locale 或 String 類型的執行個體

Locale 環境變量的作用範圍(可選)

<fmt:setLocale value="zh_TW"/>

表示設定本地環境為繁體中文。

這兩組标簽用于資源配置檔案的綁定,唯一不同的是 <fmt:bundle> 标簽将資源配置檔案綁定于它标簽體中的顯示, <fmt:setBundle> 标簽則允許将資源配置檔案儲存為一個變量,在之後的工作可以根據該變量來進行。

根據 Locale 環境的不同将查找不同字尾的資源配置檔案,這點在國際化的任何技術上都是一緻的,通常來說,這兩種标簽單獨使用是沒有意義的,它們都會與 I18N formatting 标簽庫中的其他标簽配合使用。它們的屬性和描述如表 9.18 所示:

表 9.18   <fmt:bundle> 、 <fmt:setBundle> 标簽屬性和說明

basename

資源配置檔案的指定,隻需要指定檔案名而無須擴充名,二組标簽共有的屬性

<fmt:setBundle> 獨有的屬性,用于儲存資源配置檔案為一個變量

變量的作用範圍

下面看一個示例

<fmt:setLocale value="zh_CN"/>

<fmt:setBundle basename="applicationMessage" var="applicationBundle"/>

該示例将會查找一個名為 applicationMessage_zh_CN.properties 的資源配置檔案,來作為顯示的 Resource 綁定。

用于資訊顯示的标簽,将顯示資源配置檔案中定義的資訊。它的屬性和描述如表 9.19 所示:

表 9.19   <fmt:message> 标簽屬性和說明

key

資源配置檔案的“鍵”指定

bundle

若使用 <fmt:setBundle> 儲存了資源配置檔案,該屬性就可以從儲存的資源配置檔案中進行查找

将顯示資訊儲存為一個變量

<fmt:bundle basename="applicationAllMessage">

         <fmt:message key="userName" />

         <p>

         <fmt:message key="passWord" bundle="${applicationBundle}" />

</fmt:bundle>

該示例使用了兩種資源配置檔案的綁定的做法,“ applicationMessage ”資源配置檔案利用 <fmt:setBundle> 标簽被賦于了變量“ applicationBundle ”,而作為 <fmt:bundle> 标簽定義的“ applicationAllMessage ”資源配置檔案作用于其标簽體内的顯示。

         第一個 <fmt:message> 标簽将使用“ applicationAllMessage ”資源配置檔案中“鍵”為“ userName ”的資訊顯示。

         第二個 <fmt:message> 标簽雖然被定義在 <fmt:bundle> 标簽體内,但是它使用了 bundle 屬性,是以将指定之前由 <fmt:setBundle> 标簽儲存的“ applicationMessage ”資源配置檔案,該“鍵”為“ passWord ”的資訊顯示。

<fmt:param> 标簽應該位于 <fmt:message> 标簽内,将為該消息标簽提供參數值。它隻有一個屬性 value 。

<fmt:param> 标簽有兩種使用版本,一種是直接将參數值寫在 value 屬性中,另一種是将參數值寫在标簽體内。

<fmt:requestEncoding> 标簽用于為請求設定字元編碼。它隻有一個屬性 value ,在該屬性中可以定義字元編碼。

這兩組标簽都用于設定一個時區。唯一不同的是 <fmt:timeZone> 标簽将使得在其标簽體内的工作可以使用該時區設定, <fmt:setBundle> 标簽則允許将時區設定儲存為一個變量,在之後的工作可以根據該變量來進行。它們的屬性和描述如表 9.20 所示:

表 9.20   <fmt:timeZone> 、 <fmt:setTimeZone> 标簽 屬性和說明

時區的設定

<fmt:setTimeZone> 獨有的屬性,用于儲存時區為一個變量

<fmt: formatNumber > 标 簽用于格式化數字。它的屬性和描述如表 9.21 所示:

表 9.21   <fmt:formatNumber> 标簽屬性和說明

格式化的數字,該數值可以是 String 類型或 java.lang.Number 類型的執行個體

type

格式化的類型

pattern

格式化模式

結果儲存變量

maxIntegerDigits

指定格式化結果的最大值

minIntegerDigits

指定格式化結果的最小值

maxFractionDigits

指定格式化結果的最大值,帶小數

minFractionDigits

指定格式化結果的最小值,帶小數

<fmt:formatNumber> 标簽實際是對應 java.util.NumberFormat 類, type 屬性的可能值包括 currency (貨币)、 number (數字)和 percent (百分比)。

<fmt:formatNumber value="1000.888" type="currency" var="money"/>

該結果将被儲存在“ money ”變量中,将根據 Locale 環境顯示當地的貨币格式。

<fmt:parseNumber> 标簽用于解析一個數字,并将結果作為 java.lang.Number 類的執行個體傳回。 <fmt:parseNumber> 标簽看起來和 <fmt:formatNumber> 标簽的作用正好相反。它的屬性和描述如表 9.22 所示:

表 9.22   <fmt:parseNumber> 标簽屬性和說明

将被解析的字元串

解析格式化的類型

解析格式化模式

結果儲存變量,類型為 java.lang.Number

parseLocale

以本地化的形式來解析字元串,該屬性的内容應為 String 或 java.util.Locale 類型的執行個體

<fmt:parseNumber value="15%" type="percent" var="num"/>

解析之後的結果為“ 0.15 ”。

<fmt:formatDate> 标簽用于格式化日期。它的屬性和描述如表 9.23 所示:

表 9.23   <fmt:formatDate> 标簽屬性和說明

格式化的日期,該屬性的内容應該是 java.util.Date 類型的執行個體

timeZone

指定格式化日期的時區

<fmt:formatDate> 标簽與 <fmt:timeZone> 、 <fmt:setTimeZone> 兩組标簽的關系密切。若沒有指定 timeZone屬性, 也可以通過 <fmt:timeZone> 、 <fmt:setTimeZone> 兩組标簽設定的時區來格式化最後的結果。

<fmt:parseDate> 标簽用于解析一個日期,并将結果作為 java.lang.Date 類型的執行個體傳回。 <fmt:parseDate> 标簽看起來和 <fmt:formatDate> 标簽的作用正好相反。它的屬性和描述如表 9.24 所示:

表 9.24   <fmt:parseDate> 标簽屬性和說明

結果儲存變量,類型為 java.lang.Date

以本地化的形式來解析字元串,該屬性的内容為 String 或 java.util.Locale 類型的執行個體

指定解析格式化日期的時區

<fmt:parseNumber> 和 <fmt:parseDate> 兩組标簽都實作解析字元串為一個具體對象執行個體的工作,是以,這兩組解析标簽對 var 屬性的字元串參數要求非常嚴格。就 JSP 頁面的表示層前段來說,處理這種解析本不屬于份内之事,是以 <fmt:parseNumber> 和 <fmt:parseDate> 兩組标簽應該盡量少用,替代工作的地方應該在伺服器端表示層的後段,比如在 Servlet 中。

Database access 标簽庫中的标簽用來提供在 JSP 頁面中可以與資料庫進行互動的功能,雖然它的存在對于早期純 JSP 開發的應用以及小型的開發有着意義重大的貢獻,但是對于 MVC 模型來說,它卻是違反規範的。因為與資料庫互動的工作本身就屬于業務邏輯層的工作,是以不應該在 JSP 頁面中出現,而是應該在模型層中進行。

對于 Database access 标簽庫本書不作重點介紹,隻給出幾個簡單示例讓讀者略微了解它們的功能。

Database access 标簽庫有以下 6 組标簽來進行工作: <sql:setDataSource> 、 <sql:query> 、 <sql:update> 、 <sql:transaction> 、 <sql:setDataSource> 、 <sql:param> 、 <sql:dateParam> 。

<sql:setDataSource> 标簽用于設定資料源,下面看一個示例:

<sql:setDataSource

         var="dataSrc"

         url="jdbc:postgresql://localhost:5432/myDB"

         driver="org.postgresql.Driver"

         user="admin"

         password="1111"/>

該示例定義一個資料源并儲存在“ dataSrc ”變量内。

<sql:query> 标簽用于查詢資料庫,它标簽體内可以是一句查詢 SQL 。下面看一個示例:

<sql:query var="queryResults" dataSource="${dataSrc}">

      select * from table1

</sql:query>

該示例将傳回查詢的結果到變量“ queryResults ”中,儲存的結果是 javax.servlet.jsp.jstl.sql.Result 類型的執行個體。要取得結果集中的資料可以使用 <c:forEach> 循環來進行。下面看一個示例。

<c:forEach var="row" items="${queryResults.rows}">

      <tr>

               <td>${row.userName}</td>

                   <td>${row.passWord}</td>

      </tr>

“ rows ”是 javax.servlet.jsp.jstl.sql.Result 執行個體的變量屬性之一,用來表示資料庫表中的“列”集合,循環時,通過“ ${row.XXX} ”表達式可以取得每一列的資料,“ XXX ”是表中的列名。

<sql:update> 标簽用于更新資料庫,它的标簽體内可以是一句更新的 SQL 語句。其使用和 <sql:query> 标簽沒有什麼不同。

<sql:transaction> 标簽用于資料庫的事務處理,在該标簽體内可以使用 <sql:update> 标簽和 <sql:query> 标簽,而 <sql:transaction> 标簽的事務管理将作用于它們之上。

<sql:transaction> 标簽對于事務處理定義了 read_committed 、 read_uncommitted 、 repeatable_read 、 serializable4 個隔離級别。

這兩個标簽用于向 SQL 語句提供參數,就好像程式中預處理 SQL 的“ ? ”一樣。 <sql:param> 标簽傳遞除 java.util.Date 類型以外的所有相融參數, <sql:dateParam> 标簽則指定必須傳遞 java.util.Date 類型的參數。

稱呼 Functions 标簽庫為标簽庫,倒不如稱呼其為函數庫來得更容易了解些。因為 Functions 标簽庫并沒有提供傳統的标簽來為 JSP 頁面的工作服務,而是被用于 EL 表達式語句中。在 JSP2.0 規範下出現的 Functions 标簽庫為 EL 表達式語句提供了許多更為有用的功能。 Functions 标簽庫分為兩大類,共 16 個函數。

q         長度函數: fn:length

q         字元串處理函數: fn:contains 、 fn:containsIgnoreCase 、 fn:endsWith 、 fn:escapeXml 、 fn:indexOf 、 fn:join 、 fn:replace 、 fn:split 、 fn:startsWith 、 fn:substring 、 fn:substringAfter 、 fn:substringBefore 、 fn:toLowerCase 、 fn:toUpperCase 、 fn:trim

以下是各個函數的用途和屬性以及簡單示例。

長度函數 fn:length 的出現有重要的意義。在 JSTL1.0 中,有一個功能被忽略了,那就是對集合的長度取值。雖然 java.util.Collection 接口定義了 size 方法,但是該方法不是一個标準的 JavaBean 屬性方法(沒有 get,set 方法),是以,無法通過 EL 表達式“ ${collection.size} ”來輕松取得。

fn:length 函數正是為了解決這個問題而被設計出來的。它的參數為 input ,将計算通過該屬性傳入的對象長度。該對象應該為集合類型或 String 類型。其傳回結果是一個 int 類型的值。下面看一個示例。

<%ArrayList arrayList1 = new ArrayList();

                            arrayList1.add("aa");

                            arrayList1.add("bb");

                            arrayList1.add("cc");

%>

<%request.getSession().setAttribute("arrayList1", arrayList1);%>

${fn:length(sessionScope.arrayList1)}

假設一個 ArrayList 類型的執行個體“ arrayList1 ”,并為其添加三個字元串對象,使用 fn:length 函數後就可以取得傳回結果為“ 3 ”。

fn:contains 函數用來判斷源字元串是否包含子字元串。它包括 string 和 substring 兩個參數,它們都是 String 類型,分布表示源字元串和子字元串。其傳回結果為一個 boolean 類型的值。下面看一個示例。

${fn:contains("ABC", "a")}<br>

${fn:contains("ABC", "A")}<br>

前者傳回“ false ”,後者傳回“ true ”。

9.7.3 fn:containsIgnoreCase 函數

fn:containsIgnoreCase 函數與 fn:contains 函數的功能差不多,唯一的差別是 fn:containsIgnoreCase 函數對于子字元串的包含比較将忽略大小寫。它與 fn:contains 函數相同,包括 string 和 substring 兩個參數,并傳回一個 boolean 類型的值。下面看一個示例。

${fn:containsIgnoreCase("ABC", "a")}<br>

${fn:containsIgnoreCase("ABC", "A")}<br>

前者和後者都會傳回“ true ”。

fn:startsWith 函數用來判斷源字元串是否符合一連串的特定詞頭。它除了包含一個 string 參數外,還包含一個 subffx 參數,表示詞頭字元串,同樣是 String 類型。該函數傳回一個 boolean 類型的值。下面看一個示例。

${fn:startsWith ("ABC", "ab")}<br>

${fn:startsWith ("ABC", "AB")}<br>

9.7.5  詞尾判斷函數 fn:endsWith 函數

fn:endsWith 函數用來判斷源字元串是否符合一連串的特定詞尾。它與 fn:startsWith 函數相同,包括 string 和 subffx 兩個參數,并傳回一個 boolean 類型的值。下面看一個示例。

${fn:endsWith("ABC", "bc")}<br>

${fn:endsWith("ABC", "BC")}<br>

9.7.6  字元實體轉換函數 fn:escapeXml 函數

fn:escapeXml 函數用于将所有特殊字元轉化為字元實體碼。它隻包含一個 string 參數,傳回一個 String 類型的值。

9.7.8  字元比對函數 fn:indexOf 函數

fn:indexOf 函數用于取得子字元串與源字元串比對的開始位置,若子字元串與源字元串中的内容沒有比對成功将傳回“ -1 ”。它包括 string 和 substring 兩個參數,傳回結果為 int 類型。下面看一個示例。

${fn:indexOf("ABCD","aBC")}<br>

${fn:indexOf("ABCD","BC")}<br>

前者由于沒有比對成功,是以傳回 -1 ,後者比對成功将傳回位置的下标,為 1 。

9.7.9  分隔符函數 fn:join 函數

fn:join 函數允許為一個字元串數組中的每一個字元串加上分隔符,并連接配接起來。它的參數、傳回結果和描述如表 9.25 所示:

表 9.25   fn:join 函數

參數

array

字元串數組。其類型必須為 String[] 類型

separator

分隔符。其類型必須為 String 類型

傳回結果

傳回一個 String 類型的值

<% String[] stringArray = {"a","b","c"}; %>

<%request.getSession().setAttribute("stringArray", stringArray);%>

${fn:join(sessionScope.stringArray,";")}<br>

定義數組并放置到 Session 中,然後通過 Session 得到該字元串數組,使用 fn:join 函數并傳入分隔符“ ; ”,得到的結果為“ a;b;c ”。

9.7.10  替換函數 fn:replace 函數

fn:replace 函數允許為源字元串做替換的工作。它的參數、傳回結果和描述如表 9.26 所示:

表 9.26   fn:replace 函數

inputString

源字元串。其類型必須為 String 類型

beforeSubstring

指定被替換字元串。其類型必須為 String 類型

afterSubstring

指定替換字元串。其類型必須為 String 類型

${fn:replace("ABC","A","B")}<br>

将“ ABC ”字元串替換為“ BBC ”,在“ ABC ”字元串中用“ B ”替換了“ A ”。

9.7.11  分隔符轉換數組函數 fn:split 函數

fn:split 函數用于将一組由分隔符分隔的字元串轉換成字元串數組。它的參數、傳回結果和描述如表 9.27 所示:

表 9.27   fn:split 函數

string

delimiters

指定分隔符。其類型必須為 String 類型

傳回一個 String[] 類型的值

${fn:split("A,B,C",",")}<br>

将“ A,B,C ”字元串轉換為數組 {A,B,C} 。

9.7.12  字元串截取函數 fn:substring 函數

fn:substring 函數用于截取字元串。它的參數、傳回結果和描述如表 9.28 所示:

表 9.28   fn:substring 函數

beginIndex

指定起始下标(值從 0 開始)。其類型必須為 int 類型

endIndex

指定結束下标(值從 0 開始)。其類型必須為 int 類型

${fn:substring("ABC","1","2")}<br>

截取結果為“ B ”。

9.7.14  起始到定位截取字元串函數 fn:substringBefore 函數

fn:substringBefore 函數允許截取源字元從開始到某個字元串。它的參數和 fn:substringAfter 函數相同,不同的是 substring 表示的是結束字元串。下面看一個示例。

${fn:substringBefore("ABCD","BC")}<br>

截取的結果為“ A ”。

9.7.15  小寫轉換函數 fn:toLowerCase 函數

fn:toLowerCase 函數允許将源字元串中的字元全部轉換成小寫字元。它隻有一個表示源字元串的參數 string ,函數傳回一個 String 類型的值。下面看一個示例。

${fn:toLowerCase("ABCD")}<br>

轉換的結果為“ abcd ”。

9.7.16 大寫轉換函數 fn:toUpperCase 函數

fn:toUpperCase 函數允許将源字元串中的字元全部轉換成大寫字元。它與 fn:toLowerCase 函數相同,也隻有一個 String 參數,并傳回一個 String 類型的值。下面看一個示例。

${fn:toUpperCase("abcd")}<br>

轉換的結果為“ ABCD ”。

9.7.17 空格删除函數 fn:trim 函數

fn:trim 函數将删除源字元串中結尾部分的“空格”以産生一個新的字元串。它與 fn:toLowerCase 函數相同,隻有一個 String 參數,并傳回一個 String 類型的值。下面看一個示例。

${fn:trim("AB C ")}D<br>

轉換的結果為“ AB CD ”,注意,它将隻删除詞尾的空格而不是全部,是以“ B ”和“ C ”之間仍然留有一個空格。

作為伺服器端表示層 MVC 經典架構的 Struts ,其突出表現就是在表示層頁面流轉方面。雖然在顯示的視圖層, Struts 架構提供了一組功能強大的标簽庫來幫助運用。但是這組标簽庫還是比較複雜,例如要取得一個 Session 中的 JavaBean ,需要做兩個步驟的動作。

( 1 )使用 <bean:define> 标簽來定義一個目标 JavaBean 的辨別,并從 Session 中取得源 JavaBean 賦給目标 JavaBean 。若該 JavaBean 本身是 String 類型,則隻需要設定它的 name 屬性,否則還需要設定 property 屬性。

( 2 )使用 <bean:write> 标簽将該 JavaBean 的變量屬性顯示出來。若該 JavaBean 本身是 String 類型,則隻需要設定它的 name 屬性,否則還需要設定 property 屬性。

下面看一個示例,假設 Session 中有一個參數為“ TEST ”,其值為 String 類型的字元串“ hello ”。那麼使用 Struts 架構的 <bean> 标簽庫的代碼就應該是這樣:

<bean:define id="test" name="TEST" scope="session"/>

<bean:write name="test"/>

定義一個目标 JavaBean 的辨別“ test ”,然後将從 Session 中的參數“ TEST ”所取得的源 JavaBean 的執行個體賦給目标 JavaBean 。 <bean:write> 标簽會根據 <bean:define> 标簽的 id 屬性設定自身的 name 屬性,來擷取目标 JavaBean 并顯示出來。由于它們操作的是 String 類型的字元串,是以編碼還算比較簡單。可是,如果它們操作的是一個非 String 類型的 JavaBean ,那麼編碼就比較麻煩了。

如果使用的是 JSTL ,這部分的操作就十分簡單了,僅僅通過 EL 表達式語言就可以完成了,轉換成 EL 表達式的操作編碼如下:

${sessionScope.TEST}

轉換成 JSTL ,隻要一句表達式就已經完成了 <bean> 标簽庫需要用兩個标簽和許多屬性才能完成的工作。即使使用的是 JavaBean 中的屬性, JSTL 表達式也隻需要再加個“ . ”操作符而已。

使用 JSTL 中的 EL 表達式和 JSTL 标簽庫中的标簽,可以簡化 Struts 标簽庫中許多标簽的操作。下面就根據具體的對比來進行介紹。

9.8.2 JSTL VS Struts Bean 标簽庫 :

Struts 的 Bean 标簽庫在 EL 表達式沒有出現前是十分常用的,無論從 Session 、 request 、 page 或是其他作用範圍( Scope )中取得參數、或者從标準 JavaBean 中讀取變量屬性都處理得得心應手。然而,在 EL 表達式出現之後, Struts Bean 标簽庫的标簽在操作的時候就顯示出了煩瑣的缺點。是以用 EL 表達式來替代 Struts Bean 标簽庫中的标簽是一種較好的做法。

1. <bean:define> 标簽和 <bean:write> 标簽處理顯示被 EL 表達式替換

q         原形: <bean:define> 标簽的作用是定義一個 JavaBean 類型的變量,從 Scope 源位置得到該 JavaBean 的執行個體。 <bean:write> 标簽可以通過 JavaBean 變量來做顯示的工作。

q         替換方案:利用 EL 表達式來替換。

q         示例比較

<bean:define> 标簽和 <bean:write> 标簽的動作:

<bean:define id="javaBeanName"

name="javaBeanParameter"

property="javaBeanProperty"

scope="request"/>

<bean:write name="javaBeanName"/>

EL 表達式的動作:

${requestScope.javaBeanParameter.javaBeanProperty}

${requestScope.javaBeanParameter[‘javaBeanProperty’]}

處理相同的一個動作,使用 define 标簽,通常需要記住各種屬性的功能,并有選擇地根據實際情況來挑選是否需要 property 屬性,還要指定其 scope 屬性。而 EL 表達式就友善多了,直接使用預設變量 pageScope 、 requestScope 、 sessionScope 、 applicationScope 指定源 JavaBean 作用範圍,利用“ . ”操作符來指定 JavaBean 的名稱以及利用“ [] ”或“ . ”來指定 JavaBean 中的變量屬性。

         比較結果:無論是可讀性還是程式的簡潔性方面, EL 表達式無疑要勝過許多,唯一的缺點是 EL 表達式必須使用 Servlet2.4 以上的規範。

2. <bean:cookie> 、 <bean:header> 、 <bean:parameter> 标簽和 <bean:write> 标簽處理顯示被 EL 表達式替換

         原形: <bean:cookie> 、 <bean:header> 、 <bean:parameter> 标簽的作用是,定義一個 JavaBean 類型的變量,從 cookie 、 request header 、 request parameter 中得到該 JavaBean 執行個體。 <bean:write> 标簽可以通過 JavaBean 變量來做顯示的工作。

        替換方案:利用 EL 表達式來替換。

        示例比較: <bean:parameter> 标簽的動作:

<bean:parameter id="requestString" name="requestParameterString" />

<bean:write name="requestString"/>

${param.requestParameterString}

         比較結果: EL 表達式預設的 5 個變量: cookie 、 header 、 headerValues 、 paramValues 、 param 完全可以提供更友善簡潔的操作。

3. <bean:include> 标簽被 <c:import> 标簽替換

         原形: <bean:include> 标簽的作用是定義一個 String 類型的變量,可以包含一個頁面、一個響應或一個連結。

         替換方案:利用 <c:import> 标簽來替換。

         示例比較

<bean:include> 标簽的動作:

<bean:include page="/MyHtml.html" id="thisurlPage" />

<c:import> 标簽的動作:

<c:import url="/MyHtml.html" var="thisurlPage" />

<bean:include> 标簽的 page 屬性所起的作用可以由 <c:import> 标簽來替換,二者的操作結果是一樣的。

         比較結果:這一對标簽的比較沒有明顯差別,而 <bean:include> 标簽有更多屬性提供更多功能,是以替換并不是十分必要。

尤其是當要用到配置在 struts-config.xml 中的 <global-forwards> 元素進行全局轉發頁面時,必須使用 <bean:include> 标簽的 forward 元素來實作。

4. <bean:message> 标簽處理資源配置檔案被 <fmt:bundle> 、 <fmt:setBundle> 、 <fmt:message> 标簽合作替換

         原形: <bean:message> 标簽是專門用來處理資源配置檔案顯示的,而它的資源配置檔案被配置在 struts-config.xml 的 <message-resources> 元素中。

         替換方案:利用 <fmt:bundle> 、 <fmt:setBundle> 、 <fmt:message> 标簽合作來替換,由 <fmt:bundle> 、 <fmt:setBundle> 設定資源配置檔案的實體名稱,再由 <fmt:message> 标簽負責讀取顯示。

         示例 比較

<bean:message> 标簽的動作:

<bean:message key="message.attacksolution"/>

<fmt:bundle> 、 <fmt:message> 标簽的動作:

<fmt:bundle basename="resources.application">

         <fmt:message key="message.attacksolution" />

或 <fmt:setBundle> 、 <fmt:message> 标簽的動作:

<fmt:setBundle basename="resources.application" var="resourceaApplication"/>

<fmt:message key="message.attacksolution" bundle="${resourceaApplication}"/>

q         比較結果:這一對标簽對于國際化的支援都相當好,唯一最大的差別在于利用 <bean:message> 标簽所操作的資源配置檔案是配置在 struts-config.xml 中的,而 <fmt:message> 标簽所操作的資源配置檔案則是根據 <fmt:bundle> 、 <fmt:setBundle> 兩組标簽來得到的。看起來,後者的靈活性不錯,但就筆者的眼光來看,前者更為規範,對于使用者協作的要求也更高。試想,維護一到兩個資源配置檔案與維護一大堆資源配置檔案哪個更友善呢?自然是前者了,是以除非是不依賴

Struts 架構的應用,否則最好使用 <bean:message> 标簽。

9.8.3 JSTL VS Struts Logic 标簽庫

Struts Logic 标簽庫中的标簽在頁面顯示時是時常被用到的,但是常用的卻不一定是最好用的,有了 JSTL 标簽庫和 EL 表達式後,許多 Struts Logic 标簽庫的标簽可以被簡單替換。

1. 所有判斷标簽被 EL 表達式和 <c:if> 标簽替換

q         原形:判斷标簽有一個特點,就是需要取得一個執行個體的變量,是以通過 <bean:define> 标簽來取得執行個體的變量是必須的,随後就通過各種判斷标簽來完成判斷的工作。常用的判斷标簽如表 9.30 所示:

表 9.30  常用判斷标簽

标簽名

判斷變量是否為空

notEmpty

與 empty 标簽正好相反

equal

判斷變量是否與指定的相同

notEqual

與 equal 标簽正好相反

lessThan

判斷變量是否比指定的小

greaterThan

判斷變量是否比指定的大

lessEqual

判斷變量是否小于等于指定的值

greaterEqual

判斷變量是否大于等于指定的值

present

檢查 header 、 request parameter 、 cookie 、 JavaBean 或 JavaBean propertie 不存在或等于 null 的時候,判斷成功

notPresent

與 present 标簽正好相反

match

比較 String 類型字元串是否與指定的相同

notMatch

與 match 标簽正好相反

q         替換方案:利用 EL 表達式和 <c:if> 标簽來替換。

q         示例比較:判斷标簽的動作:

property="attack_event_code"

<logic:notEmpty name="javaBeanParameter">

         javaBeanParameter not empty

</logic:notEmpty>

EL 表達式和 <c:if> 标簽的動作:

<c:if test="${requestScope.javaBeanParameter.attack_event_code != null

&& requestScope.javaBeanParameter.attack_event_code != ‘‘”}>

</c:if>

EL 表達式利用操作符來完成判斷動作,然後通過 <c:if> 标簽來根據判斷結果處理對應工作。

q         比較結果: EL 表達式的操作符對判斷的貢獻很大, EL 表達式的靈活性是 Struts 判斷标簽無法比拟的,任何判斷标簽都可以通過表達式來實作。 <c:if> 标簽還可以将判斷的結果儲存為一個變量,随時為之後的頁面處理服務。

反觀 Struts 架構的判斷标簽,在工作之前必須先定義被判斷的變量,而判斷後又無法儲存判斷結果,這樣的程式設計遠不如 EL 表達式和 <c:if> 标簽的協作來得強大。是以使用 EL 表達式和 <c:if> 标簽來替換判斷标簽是更好的選擇。

2. <logic:iterate> 标簽被 <c:forEach> 标簽和 EL 表達式替換

         原形: <logic:iterate> 标簽用來對集合對象的疊代,可以依次從該集合中取得所需要的對象。

         替換方案:利用 <c:forEach> 标簽和 EL 表達式的協作替換 <logic:iterate> 标簽。

<logic:iterate> 标簽的動作:

<logic:iterate name="allAttackSolution"

     id="attackSolution"

     type="struts.sample.cap1.sample3.entity.AttackSolution">

         <bean:write property="attack_event_code" name="attackSolution"/>

         <bean:write property="attack_mean" name="attackSolution"/>

         <bean:write property="attack_action" name="attackSolution"/>

</logic:iterate>

<c:forEach> 标簽 EL 表達式協作的動作:

<c:forEach items="${requestScope.allAttackSolution}" var="attackSolution">

         ${attackSolution.attack_event_code}

         ${attackSolution.attack_mean}

         ${attackSolution.attack_action}

兩個動作都做的是同一件事,從 request 中得到儲存的“ allAttackSolution ”參數,該參數為一個集合,集合中的對象為 struts.sample.cap1.sample3.entity.AttackSolution 類型的執行個體。

<logic:iterate> 标簽本身可以接收集合,儲存為一個變量,利用疊代子模式,使 <logic:iterate> 标簽體中的 <bean:write> 标簽将集合中的每個 JavaBean 顯示出來。

提示:在本例中由于要顯示 JavaBean 中的變量屬性,是以 <bean:write> 标簽還需要設定 property 屬性。

替換工作的 <c:forEach> 标簽則相對要友善些, items 屬性使用 EL 表達式取得集合,然後設定 var 屬性作為集合中對象的變量,最後使用 EL 表達式來顯示資料。

         比較結果:

值得注意的一個地方是, <logic:iterate> 标簽必須為集合中的對象指定類型,因為标簽庫處理時會将集合中的對象作為 Object 類型得到,然後需要讀取 type 屬性定義的 Java 類為它強制轉型。

而 <c:forEach> 标簽則完全不用,隻要符合标準 JavaBean (為變量屬性提供 get 、 set 方法)的對象都可以通過 EL 表達式來從 var 屬性定義的變量中取得該 JavaBean 的變量屬性。

是以 <c:forEach> 标簽和 EL 表達式的方式更加簡單,也更加靈活。

當然,熟悉 <logic:iterate> 标的程式設計者也可以将 <bean:write> 标簽替換為 EL 表達式而仍然使用 <logic:iterate> 标簽。代碼可以是這樣:

id="attackSolution"

type="struts.sample.cap1.sample3.entity.AttackSolution">

結果一樣,但這種方式比 <bean:write> 标簽顯示方式靈活多了。

3. <logic:redirect> 标簽被 <c:redirect> 和 <c:param> 标簽替換

         原形: <logic:redirect> 标簽用來轉發到一個頁面,并可以為轉發傳遞參數。

         替換方案:利用 <c:redirect> 和 <c:param> 标簽的協作替換 <logic:redirect> 标簽。

         示例比較: <logic:iterate> 标簽的動作:

<%

         HashMap paramMap = new HashMap();

         paramMap.put("userName", "RW");

         paramMap.put("passWord", "123456");

 <logic:redirect page="/MyHtml.jsp" name="paramMap" scope="request" />

<c:redirect> 和 <c:param> 标簽協作的動作:

         <c:param name="userName" value="RW"/>

         <c:param name="passWord" value="123456"/>

兩個動作都做的是同一件事,都将轉發到目前 Web Context 下的“ MyHtml.jsp ”去,而且都将為它提供兩個參數。最後的轉發連結看起來應該如下所示:

http://localhost:8080/test/ MyHtml.jsp? userName=RW&password=123456

q         比較結果

一眼就可以看出, <logic:redirect> 标簽的可讀性不強,它的 name 屬性表示的是一個 Map 類型的變量。如果還有 property 屬性,則 name 屬性指的是一個标準 JavaBean 。 property 屬性指的是 JavaBean 中的一個 Map 類型的變量屬性,通過 Map 的“名值對”來為轉發頁面傳遞參數。如果轉發參數是來自于一個 Map 或 JavaBean 中的 Map 類型變量屬性,那還好,因為可以在 Java 類中處理。可是如果純粹是從頁面上取得某些值作為轉發參數,那就困難了,必須像本示例所給出的那樣,自行定義一個

Map 執行個體。這種情況下,頁面就會看到 Java 語言的片段,既麻煩又不符合标準。

而使用 <c:redirect> 和 <c:param> 标簽協作,由于包含在 <c:redirect> 标簽體内的 <c:param> 标簽可以有多個,是以顯式地提供 <c:param> 标簽就完成了給出轉發參數的工作,即使用到 JavaBean ,也可以使用 EL 表達式來實作。

綜上所述,利用 <c:redirect> 和 <c:param> 标簽來代替 <logic:redirect> 标簽是有必要的。

9.8.4  總結

Struts 架構和 JSTL 并不是互相沖突的兩種技術,雖然 Struts 架構提供了功能不錯的标簽庫,但是使用 JSTL 可以簡化 Struts 架構标簽庫複雜的地方,這對于伺服器端表示層架構的 Struts 來說幫助很大。 Struts 的 HTML 标簽庫無法使用 JSTL 來替換,但是,使用 EL 表達式作為一些 value 屬性,來做指派的工作仍然不失為一種好的選擇。是以,在 JSTL 已經比較成熟的今天,使用 Struts 架構和 JSTL 整合來作 JSP 頁面将使程式設計更為輕松。

在這一小節中,将修改在第三章中曾經給出的 Struts 架構示例,以 Struts 架構和 JSTL 的協同工作來實作。

對于第三章的示例,要将 JSTL 整合進去,需要做以下幾步工作。

( 1 )下載下傳 JSTL 并配置。

( 1 )修改原先的 web.xml 使其作為 Servlet2.4 的實作。

( 3 )修改 JSP 顯示頁面,整合 JSTL 和 Struts 标簽庫一起工作。

9.9.1  下載下傳 JSTL 并配置

可以從 http://java.sun.com/products/jsp/jstl 網址中下載下傳 JSTL1.1 的最新版本。要使用這些标簽庫需要做 3 個步驟的工作。

( 1 )将下載下傳的 jstl.jar 放置到 Web 應用的 WEB-INF 的 lib 目錄下。

( 2 )将下載下傳的 TLD 檔案放置到 Web 應用的 WEB-INF 目錄下。

( 3 )在需要使用的 JSP 頁面中聲明該标簽庫。

9.9.2  修改 web.xml 使其作為 Servlet2.4 的實作

在第三章的示例中所給出的 web.xml 是 Servlet2.3 規範的,是以無法很好的支援 JSTL1.1 ,要修改為符合 Servlet2.4 規範的代碼。使 web.xml 成為 Servlet2.4 規範是十分容易的,需要修改的是其頭部 DTD 聲明。

在 Servlet2.3 之前,校驗和規範 web.xml 都是使用 DTD ,是以其頭部聲明如下:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

</web-app>

而到了 Servlet2.4 規範,首此使用了 xmlns 來聲明 web.xml ,是以其頭部聲明為:

<web-app version="2.4"

         xmlns="http://java.sun.com/xml/ns/j2ee"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd ">

是以,為了支援 Servlet2.4 規範,應該将第三章示例的 web.xml 改成如例 9.6 的樣子。

例 9.6 :修改後的 web.xml

修改的地方不多,僅僅是頭部的聲明。其他地方完全不必改動,這樣的 web.xml 已經支援了 Servlet2.4 規範了。

9.9.3 JSP 部分: <logic:notEmpty> 和 <c:if> 标簽

對于 JSP 顯示頁面的修改是整合的核心部分,在第三章示例的 showAttackSolution.jsp 中出現了這樣的語句:

<logic:notEmpty name="allAttackSolution">

...

将類型為 ArrayList 的變量“ allAttackSolution ”從作用範圍中取出,利用 <logic:notEmpty> 标簽判斷該 ArrayList 是否為空。

根據之前讨論的“所有判斷标簽被 EL 表達式和 <c:if> 标簽替換”,可以利用 <c:if> 标簽和 EL 表達式來修改該段 JSP 代碼。

修改後的結果如下:

<c:if test="${(requestScope.allAttackSolution != null)

&& fn:length(requestScope.allAttackSolution) != 0}">

<logic:notEmpty> 标簽其本身具有多種功能:

q         一是判斷是否為 null 。

q         二是當它為 String 類型的變量時,判斷字元串長度是否為 0 。

q         三是當它為集合類型的變量時,利用集合類的 isEmpty 方法可以判斷是否是一個空的集合。

本示例既然要在替換後與替換前的工作一緻,就應該對集合做兩個判斷:

q         一是該集合不為 null 。

q         二是該集合中的對象數量不為 0 。

“ !=null ”的 EL 表達式實作了集合執行個體不為 null 的判斷; fn:length() 函數實作了集合内對象數量不為 0 的判斷,兩個判斷用“ && ”連接配接起來就實作了 <logic:notEmpty> 标簽對于集合判斷的工作。

在這裡應該利用“ <logic:notEmpty> 标簽”,還是利用“ EL 表達式和 <c:if> 标簽”呢? <logic:notEmpty> 标簽相對來說可讀性更強些, EL 表達式作為判斷條件則可讀性稍差些。然而,這些僅是就本示例的改動而言的,其他情況下,利用 EL 表達式和 <c:if> 标簽還是有其優勢的。

9.9.4 JSP 部分: <logic:iterate> 和 <c:forEach> 标簽

在第三章示例的 showAttackSolution.jsp 中出現了這樣的使用:

         <tr>

                   <td style="word-break: break-all;">

                            <bean:write property="attack_event_code" name="attackSolution" />

                   </td>

                            <bean:write property="attack_mean" name="attackSolution" />

                            <bean:write property="attack_action" name="attackSolution" />

                            <input type="button"

                    onclick="del(‘<%=attackSolution.getAttack_event_code()%>‘);"

value="<bean:message key="message.delete"/>">

         </tr>

由于在 Action 中将顯示的内容作為 ArrayList 類型的執行個體儲存在 request 中,是以這段 JSP 頁面标簽的工作是:

( 1 )利用 <logic:iterate> 标簽對儲存在 ArrayList 執行個體中的所有對象進行循環取得。

( 2 ) ArrayList 類型執行個體中的對象為 struts.sample.cap1.sample3.entity.AttackSolution 類型, AttackSolution Java 類中的變量屬性都有 get 、 set 方法,是以可以被認為是一個标準的 JavaBean 。利用 <bean:write> 标簽将 AttackSolution 執行個體的變量屬性讀取出來,并顯示。

根據之前讨論的“ <logic:iterate> 标簽被 <c:forEach> 标簽和 EL 表達式替換”,可以利用 <c:forEach> 标簽和 EL 表達式來修改該段 JSP 代碼。修改的方式有兩種:

         完全使用 <c:forEach> 标簽和 EL 表達式來替換全部。

         僅使用 EL 表達式來替換 <bean:write> 标簽。

1. <c:forEach> 标簽和 EL 表達式

<c:forEach> 标簽和 EL 表達式:

<c:forEach items="${requestScope.allAttackSolution}"

var="attackSolution">

                  <td style="word-break: break-all;" >

                            ${attackSolution.attack_event_code}

                   <td style="word-break: break-all;" >

                            ${attackSolution.attack_mean}

                            ${attackSolution.attack_action}

<input type="button"

onclick="del(‘${attackSolution.attack_event_code}‘);"

</td>

這種修改方式将屏棄 Struts 架構的 <logic:iterate> 标簽,而以 <c:forEach> 标簽來作為循環疊代的工作。它的最大優點是無需關注集合中的對象類型,隻要保證該對象是一個标準的 JavaBean 就可以了。

2. 使用 EL 表達式來替換 <bean:write> 标簽

                       ${attackSolution.attack_event_code}

                       ${attackSolution.attack_mean}

                       ${attackSolution.attack_action}

這種方式對原來的代碼沒有做多大的改動,依然會使用 <logic:iterate> 标簽來作為循環标簽。不過對于原來使用 <bean:write> 标簽做顯示功能的地方,摒棄了 <bean:write> 标簽而直接使用 EL 表達式。靈活的 EL 表達式對頁面顯示邏輯有很大幫助,這種方式比較适合熟悉 <logic:iterate> 标簽的程式設計者。

9.9.5  完整的 JSP

下面看一個完整的修改後 JSP 頁面的代碼,注釋掉的是被替換之前的代碼,讀者可以比較一下兩種實作方法。請見例 9.7 。

例 9.7 :修改後 showAttackSolution.jsp 。

可以看到,在這個被修改的 JSP 頁面代碼中,利用了 Struts 架構提供的标簽來實作送出部分的工作以及國際化資源配置檔案讀取顯示的工作,也利用 JSTL 的标簽庫和 EL 表達式來實作頁面邏輯部分的工作。

在 JSP 頁面使用 JSTL 是一種規範,也是一件令人興奮的事情,因為它使 JSP 部分的程式設計變得更加有效合理。

繼續閱讀