流程控制标签
要执行流程控制逻辑,页面编写者一般必须采用scriptlet。例如,下列scriptlet用于对购物车中的产品进行迭代:
<%
Iterator i = cart.getItems().iterator();
while (i.hasNext()) {
ShoppingCartItem item =
(ShoppingCartItem)i.next();
...
%>
<tr>
<td align="right"
bgcolor="#ffffff">
<%=item.getQuantity()%>
</td>
...
<%
}
%>
流程控制标签消除了对scriptlet的需要。下面两节中的例子展示条件和迭代器标签。
条件标签
if标签可以根据一个测试属性的值有条件地执行其正文。下面catalog.jsp中的例子测试请求参数Add是否为空。如果测试结果为true,那么页面在数据库中查询由请求参数标识的图书记录并将书添加到购物车上。
<c:if test="${!empty param.Add}">
<c:set var="bid" value="${param.Add}"/>
<jsp:useBean id="bid"
type="java.lang.String" />
<sql:query var="books"
dataSource="${applicationScope.bookDS}">
select * from PUBLIC.books where
id = ?
<sql:param value="${bid}"
/>
</sql:query>
<c:forEach var="bookRow" begin="0"
items="${books.rows}">
<jsp:useBean id="bookRow"
type="java.util.Map" />
<jsp:useBean id="addedBook"
class="database.BookDetails"
scope="page" />
...
<% cart.add(bid, addedBook); %>
...
</c:if>
choose标签通过嵌入when子标签有条件地执行代码块。它呈现测试条件判断为true的第一个when标签的正文。如果嵌入的when标签的测试条件都没有判断为true,那么就判断otherwise标签中的正文(如果有的话)。
例如,下面示例代码显示如何根据客户端的成员资格类别呈现文字。
<c:choose>
<c:when test="${customer.category == 'trial'}"
>
...
</c:when>
<c:when test="${customer.category
== 'member'}" >
...
</c:when>
<c:when test="${customer.category
== 'preferred'}" >
...
</c:when>
<c:otherwise>
...
</c:otherwise>
</c:choose>
可以像下面这样用choose、when和otherwise标签构建if-then-else语句:
<c:choose>
<c:when test="${count == 0} >
No records matched your selection.
</c:when>
<c:otherwise>
<c:out value="${count}"/>
records matched your selection.
</c:otherwise>
</c:choose>
迭代器标签
forEach标签使您可以对一组对象进行迭代。通过items属性指定集合,通过由item属性命名的作用域变量可以得到当前项。
forEach支持很多种集合类型,包括java.util.Collection
and java.util.Map的所有实现。如果items属性的类型为java.util.Map,那么当前项的类型就是java.util.Map.Entry,它有下面的属性:
· key –储存在底层Map中的项的键
· value – 对应于该键的值
也支持对象数组以及基本类型(如int)数组。对于基本类型数组,迭代的当前项自动用其包装器类包装(例如,对于int是Integer,对于float是Float,等等)
支持java.util.Iterator
and java.util.Enumeration的实现,但是必须小心使用。Iterator和Enumeration对象是不可重设的,所以不应该在多个迭代器标签中使用它们。最后,如果字符串包含一组由逗号分隔的值(如:Monday、Tuesday、Wednesday、Thursday、Friday),则可以迭代java.lang.String对象。
下面是用forEach标签对前一节中的购物车迭代:
<c:forEach var="item" items="${sessionScope.cart.items}">
...
<tr>
<td align="right"
bgcolor="#ffffff">
<c:out value="${item.quantity}"/>
</td>
...
</c:forEach>
用forEach标签对由分隔符分隔的一组标记(token)进行迭代。
URL标签
提供jsp:include元素是为了在当前页面的同一上下文中加入静态和动态资源。不过,
jsp:include不能访问在Web应用程序之外的资源,并在加入的资源被另一个元素使用时产生不必要的缓冲。
在下面的例子中,transform元素使用加入的资源的内容作为其转换的输入。jsp:include元素读取响应的内容,将它写入外围转换元素的正文内容中,然后该外围元素再重新读取这些完全一样的内容。如果元素可以直接访问输入资源,那么效率就会更高且可避免转换标签的正文内容中所涉及的缓冲。
<acme:transform>
<jsp:include page="/exec/employeesList"/>
<acme:transform/>
因而import标签是简单的、一般性的访问基于URL的资源的方式,而该资源的内容将被加入到JSP页面中或者在其中处理。例如,在XML标签中,使用import读取包含图书信息的XML文档并将内容指定给范围变量XML:
<c:import url="/books.xml"
var="xml" />
<x:parse xml="${xml}" var="booklist"
scope="application" />
与jsp:param标签(见jsp:param元素)一样,param标签可以与import一同使用以指定请求参数。
在会话跟踪中,我们讨论了应用程序是如何必须重写URL,以便在客户端关闭cookie时可以进行会话跟踪的。可以使用url标签重写从JSP页面返回的URL。只有当禁用cookie时,标签才会在URL中的加入会话ID,否则它返回未加改变的URL。注意这项功能要求URL是相对的。url标签取param子标签以得到加入返回的URL中的参数。例如,catalog.jsp重写所使用的URL以向购物车中增加一本书,如下所示:
<c:url var="url"
value="/catalog" >
<c:param name="Add" value="${bookId}"
/>
</c:url>
<p><strong><a href="<c:out value='${url}'/>">
redirect标签向客户端发送一个HTTP重定向。redirect标签用param子标签在返回的URL中加入参数。
XML标签
处理XML文档的一个关键方面是能够容易地访问它们的内容。Xpath是W3C1999年提出的一个建议,它提供了一种指定和选择XML文档内容的方便符号。在表17-4中列出的JSTL
XML标签集基于XPath(见XPath如何工作)。
| 表
17-4 XML标签 |
| 领域 |
功能 |
标签 |
TLD
|
前缀 |
| XML |
核心 |
out
parse
set |
/jstl-x |
x |
| 流程控制 |
choose
when
otherwise
forEach
if |
| 转换 |
transform
param |
XML标签使用XPath作为local
表达式语言,总是用select属性指定XPath表达式。这意味着只有为select属性指定的值是用XPath表达式语言判断的。所有其他属性都是用与全局表达式语言相关的规则判断的。
除了标准XPath语法,JSTL XPath引擎还支持下面作用域以在XPath表达式中访问Web应用程序数据:
· $foo
· $param:
· $header:
· $cookie:
· $initParam:
· $pageScope:
· $requestScope:
· $sessionScope:
· $applicationScope:
这些作用域的定义方式与在隐式对象中讨论的JSTL表达式语言中的作用域完全相同。表17-5显示了一些使用作用域的例子。
| 表
17-5 示例XPath表达式 |
| XPath表达式 |
结果 |
| $sessionScope:profile
|
会话范围的属性称为profile |
| $initParam:mycom.productId
|
上下文参数mycom.productId的String值
|
在Duke's Bookstore应用程序的另一个版本(bookstore5)中展示了这些XML标签。这个版本用XML表达的书店数据库(books.xml)替换了数据库。要编译和安装这种版本的应用程序,按照JSP页面示例中的指示,用bookstore5替换bookstore4。
因为XML标签要求XPath判断,所以Java
WSDP在<JWSDP_HOME>/jstl-1.0.3/standard中的两个库jaxen-full.jar和saxpath.jar中包括Jaxen
XPath判断程序。在编译Duke's Bookstore应用程序时,这些库自动拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore5/build/WEB-INF/lib中。
核心标签
核心XML标签提供方便地解析和访问XML数据的基本功能。
parse标签解析XML文档并将得到的对象保存在由var属性指定的作用域属性中。在bookstore5中,解析XML文档并保存到parseBooks.jsp中的上下文属性,所有需要访问该文档的JSP页面都包含它。
<c:if test="${applicationScope:booklist
== null}" >
<c:import url="/books.xml"
var="xml" />
<x:parse xml="${xml}" var="booklist"
scope="application" />
</c:if>
out和set标签与在表达式标签中描述的XPath本地表达式语言的行为相同。out标签判断当前上下文节点上的XPath表达式并将判断结果输出到当前JspWriter对象上。
set标签判断XPath表达式并将结果设置到属性var指定的JSP作用域属性。
JSP页面bookdetails.jsp选择属性id与请求参数bookId相匹配的book元素并设置abook属性。out标签再选择book的title元素并输出结果。
<x:set var="abook"
select="$applicationScope.booklist/
books/book[@id=$param:bookId]"
/>
<h2><x:out select="$abook/title"/></h2>
正如您所看到的,x:set储存一个用XPath表达式获取的节点的内部XML表达,它不将所选的节点转换为String并储存它。因此,x:set主要用于储存文档的内容以便以后获取。
如果希望储存String,那么需要在c:set中使用x:out。x:out标签将节点转换为String,而c:set>
t随后将String作为作用域属性储存。例如,bookdetails.jsp储存包含图书价格的作用域属性,以后它会加入fmt标签中,如下所示:
<c:set var="price">
<x:out select="$abook/price"/>
</c:set>
<h4><fmt:message key="ItemPrice"/>:
<fmt:formatNumber value="${price}"
type="currency"/>
更直接但是需要用户对XPath更多知识的另一种选择是,手工用XPath的string功能将节点强行转换为string。
<x:set var="price" select="string($abook/price)"/>
流程控制标签
XML流程控制标签与在流程控制标签中描述的XPath表达式语言的行为一样。
JSP页面catalog.jsp使用forEach标签显示在booklist中包含的所有图书。如下所示:
<x:forEach var="book"
select="$applicationScope:booklist/books/*">
<tr>
<c:set var="bookId">
<x:out select="$book/@id"/>
</c:set>=
<td bgcolor="#ffffaa">
<c:url var="url"
value="/bookdetails"
>
<c:param
name="bookId" value="${bookId}"
/>
<c:param
name="Clear" value="0" />
</c:url>
<a href="<c:out
value='${url}'/>">
<strong><x:out
select="$book/title"/>
</strong></a></td>
<td bgcolor="#ffffaa"
rowspan=2>
<c:set var="price">
<x:out
select="$book/price"/>
</c:set>
<fmt:formatNumber
value="${price}" type="currency"/>
</td>
<td bgcolor="#ffffaa"
rowspan=2>
<c:url var="url"
value="/catalog" >
<c:param name="Add"
value="${bookId}" />
</c:url>
<p><strong><a
href="<c:out value='${url}'/>">
<fmt:message
key="CartAdd"/> </a>
</td>
</tr>
<tr>
<td bgcolor="#ffffff">
<fmt:message
key="By"/> <em>
<x:out select="$book/firstname"/>
<x:out select="$book/surname"/></em></td></tr>
</x:forEach>
转换标签
transform标签对XML文档进行转换,这种转换是xslt属性设置的XSLT样式表指定的。如果没有指定xml属性,那么从标签的正文内容中读取输入XML文档。
可以将用param子标签与transform一起使用以设置转换参数。属性name和value用于指定参数。value属性是可选的:如果没有指定它,那么就从标签的正文提取值。
国际化标签
在国际化和本地化Web应用程序中我们讨论了如何调整Web应用程序以使用客户端地区的语言和格式规范。本节描述支持国际化JSP页面的标签。
JSTL为以下功能定义了标签:
· 设置页面的地区
· 创建符合地区习惯的消息
· 以符合地区习惯的格式对数据元素,如数字、货币、日期和时间这样的数据进行编排和解析。
| 表17-6
国际化标签 |
| 领域 |
功能 |
标签 |
TLD
|
前缀 |
| I18n |
设置地区 |
setLocale
requestEncoding |
/jstl-fmt |
fmt |
| 消息 |
bundle
message
param
setBundle |
| 编排数字和日期格式 |
formatNumber
formatDate
parseDate
parseNumber
setTimeZone
timeZone |
设置地区
setLocale标签用于改变客户端为页面指定的地区。requestEncoding标签用于设置请求的字符编码,以便可以正确地解析不是由ISO-8859-1编码的请求参数。
消息标签
在默认情况下,浏览器可以检测到所使用的地区。这意味着由客户端(通过浏览器设置)决定使用哪种地区,并使页面编写者可以迎合其客户端所选择的语言。
bundle 标签
使用bundle标签指定页面上绑定的资源。
要定义Web应用程序上的资源绑定,需要在Web应用程序部署描述符中指定上下文参数javax.servlet.jsp.jstl.fmt.localizationContext。下面是Duke's
Bookstore描述符的声明:
<context-param>
<param-name>
javax.servlet.jsp.jstl.fmt.localizationContext
</param-name>
<param-value>messages.BookstoreMessages</param-value>
</context-param>
message标签
message标签用于输出符合地区习惯的字符串。下面是catalog.jsp中的标签
<h3><fmt:message key="Choose"/></h3>
它用于输出邀请客户端从目录中选择图书的字符串。
param子标签向其父标签message中的组合消息或者样式提供一个参数(用于参数替换)。必须为组合消息或者样式中的每一个变量指定一个param标签。参数替换以param标签的顺序发生。
Formatting 标签
JSTL提供了一组标签用于解析和编排符合地区习惯的数字和日期格式。
formatNumber标签用于输出区域化的数字。下面是showcart.jsp中的标签
<fmt:formatNumber value="${book.price}"
type="currency"/>
它用于显示图书的符合地区习惯的价格。注意由于价格在数据库中是以美元维护的,本地化因而是简化的,因为formatNumber标签不知道兑换率。标签编排货币的格式,但是不转换它们。
还有编排日期格式(formatDate)和解析数字和日期(parseNumber,
parseDate)的类似标签。timeZone标签建立了任何所有的formatDate标签使用的时区(通过value属性指定)。
在receipt.jsp中,创建了一个“虚构”的发货日期,然后用formatDate标签编排它的格式:
<jsp:useBean id="now" class="java.util.Date"
/>
<jsp:setProperty name="now" property="time"
value="<%= now.getTime() + 432000000
%>" />
<fmt:message key="ShipDate"/>
<fmt:formatDate value="${now}" type="date"
dateStyle="full"/>.
SQL
标签
JSTL SQL标签是为快速建立原型和简单应用程序设计的。对于生产应用程序,数据库操作通常封装在JavaBeans组件中。
| 表17-7
SQL 标签 |
| 领域 |
函数 |
标签 |
TLD
|
前缀 |
| 数据库 |
|
setDataSource |
/jstl-sql |
sql |
| SQL |
query
dateParam
param
transaction
update
dateParam
param |
setDataSource标签是为了让您可以设置数据库的数据源信息而提供的。可以提供JNDI名或者DriverManager参数以设置数据源信息。所有拥有多个SQL标签的Duke's
Bookstore页面都使用下面的语句设置数据源:
<sql:setDataSource dataSource="jdbc/BookDB"
/>
query标签用于执行返回结果集的SQL查询。对于带参数的SQL查询,使用嵌入在query标签中的param标签。
在catalog.jsp中,请求参数Add的值确定应该从数据库中提取哪一本图书的信息。这个参数保存为名为bid的属性并传递给param标签。注意query标签从上下文监听器中设置的上下文属性bookDS中获得其数据源。
<c:set var="bid" value="${param.Add}"/>
<sql:query var="books" >
select * from PUBLIC.books where id = ?
<sql:param value="${bid}" />
</sql:query>
update标签用于更新数据库中的一行。transaction标签用于自动执行一系列SQL语句。
JSP页面receipt.jsp同时使用这两种标签,对每一次采购更新数据库库存。因为购物车可能包含多本书,所以用transaction标签包装多个查询和更新。首先页面确认有足够的库存,然后进行更新。
<c:set var="sufficientInventory" value="true"
/>
<sql:transaction>
<c:forEach var="item" items="${sessionScope.cart.items}">
<c:set var="book"
value="${item.item}" />
<c:set var="bookId"
value="${book.bookId}" />
<sql:query var="books"
sql="select
* from PUBLIC.books where id = ?" >
<sql:param
value="${bookId}" />
</sql:query>
<jsp:useBean id="inventory"
class="database.BookInventory"
/>
<c:forEach var="bookRow"
begin="0"
items="${books.rowsByIndex}">
<jsp:useBean
id="bookRow" type="java.lang.Object[]"
/>
<jsp:setProperty
name="inventory" property="quantity"
value="<%=(Integer)bookRow[7]%>"
/>
<c:if
test="${item.quantity > inventory.quantity}">
<c:set
var="sufficientInventory" value="false"
/>
<h3><font
color="red" size="+2">
<fmt:message
key="OrderError"/>
There
is insufficient inventory for
<i><c:out
value="${bookRow[3]}"/></i>.</font></h3>
</c:if>
</c:forEach>
</c:forEach>
<c:if test="${sufficientInventory
== 'true'}" />
<c:forEach var="item"
items="${sessionScope.cart.items}">
<c:set var="book"
value="${item.item}" />
<c:set var="bookId"
value="${book.bookId}" />
<sql:query
var="books"
sql="select
* from PUBLIC.books where id = ?" >
<sql:param
value="${bookId}" />
</sql:query>
<c:forEach
var="bookRow" begin="0"
items="${books.rows}">
<sql:update
var="books" sql="update PUBLIC.books
set
inventory
= inventory - ? where id = ?" >
<sql:param
value="${item.quantity}" />
<sql:param
value="${bookId}" />
</sql:update>
</c:forEach>
</c:forEach>
<h3><fmt:message
key="ThankYou"/>
<c:out value="${param.cardname}"
/>.</h3><br>
</c:if> </sql:transaction>
查询标签结果接口
Result接口用于从query标签返回的对象上提取信息。
public interface Result
public String[] getColumnNames();
public int getRowCount()
public Map[] getRows();
public Object[][] getRowsByIndex();
public boolean isLimitedByMaxRows();
有关这个接口的完整信息,见javax.servlet.jsp.jstl.sql包的API文档。
由query标签设置的var属性的类型为Result。getRows方法返回一个映射数组,它可以提供给forEach标签的items属性。JSTL表达式语言将语法${result.rows}转换为对result.getRows的调用。下面例子中的表示式${books.rows}返回一个映射数组。
在向forEach标签提供映射数组时,由这个标签设置的var属性的类型是Map。要从一行中提取数据,使用get("colname")方法以得到列值。JSTL表达款语言将语法${map.colname}转换为对map.get("colname")的调用。例如,表达式${book.title}返回图书映射的标题项的值。
Duke's Bookstore页面bookdetails.jsp从book映射中提取列值,如下所示。
<c:forEach var="book" begin="0"
items="${books.rows}">
<h2><c:out value="${book.title}"/></h2>
<fmt:message key="By"/>
<em><c:out
value="${book.firstname}"/>
<c:out
value="${book.surname}"/></em>
(<c:out value="${book.year}"/>)<br>
<br>
<h4><fmt:message key="Critics"/></h4>
<blockquote><c:out value="${book.description}"/>
</blockquote>
<h4><fmt:message key="ItemPrice"/>:
<fmt:formatNumber value="${book.price}"
type="currency"/>
</h4>
</c:forEach>
下面内容取自catalog.jsp,它使用Row接口用脚本语言表达式从图书行中各列提取值。首先从数据库中提取匹配请求参数(bid)
的图书行。由于bid和bookRow对象在后面由标签用于以脚本语言表达式设置属性值和以scriptlet将图书添加到购物车,这两个对象都用jsp:useBean标签声明为脚本变量。页面创建一个描述图书的bean,用脚本语言表达式根据图书行列值设置图书属性。最后,将图书添加到购物车中。
可能希望将这个版本的catalog.jsp与JavaServer
Pages技术与JSP页面中的自定义标签中使用图书数据库JavaBean组件的版本进行对比。
<sql:query var="books"
dataSource="${applicationScope.bookDS}">
select * from PUBLIC.books where id = ?
<sql:param value="${bid}" />
</sql:query> <c:forEach var="bookRow"
begin="0"
items="${books.rowsByIndex}">
<jsp:useBean id="bid"
type="java.lang.String" />
<jsp:useBean id="bookRow" type="java.lang.Object[]"
/>
<jsp:useBean id="addedBook"
class="database.BookDetails"
scope="page" />
<jsp:setProperty name="addedBook"
property="bookId"
value="<%=bookRow[0]%>"
/>
<jsp:setProperty name="addedBook"
property="surname"
value="<%=bookRow[1]%>"
/>
<jsp:setProperty name="addedBook"
property="firstName"
value="<%=bookRow[2]%>"
/>
<jsp:setProperty name="addedBook"
property="title"
value="<%=bookRow[3]%>"
/>
<jsp:setProperty name="addedBook"
property="price"
value="<%=((Double)bookRow[4]).floatValue()%>"
/>
<jsp:setProperty name="addedBook"
property="year"
value="<%=(Integer)bookRow[5]%>"
/>
<jsp:setProperty name="addedBook"
property="description"
value="<%=bookRow[6]%>" />
<jsp:setProperty name="addedBook"
property="inventory"
value="<%=(Integer)bookRow[7]%>"
/>
</jsp:useBean> <% cart.add(bid,
addedBook); %>
...
</c:forEach>
更多信息
有关JSTL的更多信息参见:
· 下面地址中Java WSDP的参考文档<JWSDP_HOME>/jstl-1.0.3/docs/index.html。
· Java WSDP中的JSTL
示例。运行Tomcat时,可以在下面地址中访问这些例子http://localhost:8080/jstl-examples/index.html。
· 以下Web站点列出的资源http://java.sun.com/products/jsp/jstl。
· 有关JSTL的完整语法和语义描述见
JSTL
1.0 规范。
|