JavaServer Pages标准标签库
Stephanie Bodoff

摘 :http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/jpstl.17/17.1.htm

JavaServer Pages标准标签库(JSTL)封装了许多JSP应用程序共有的核心功能。例如,不需要用脚本或者众多不同厂商的不同迭代器标签对列表进行迭代,JSTL定义了在所有地方都可以使用的标准标签。这种标准化使您可以学习一种标签后,在多个JSP容器中使用它。而且,当标签标准化以后,容器可以优化对它们的实现。

JSTL支持通用的、结构化的任务,如迭代和条件、操作XML文档的标签、国际化标签以及以及用SQL访问数据库的标签。它还引入了表达式语言的概念以简化页面的开发。JSTL还提供了集成现有标签库与JSTL的框架。

本章通过在前面几章讨论的Duke's Bookstore应用程序的JSP版本的内容展示JSTL。假定您已经熟悉了第16章中的“使用标签”中的内容。

JSP 页面示例

本章通过如下重新编写JSP版本的、在第16章讨论过的Duke's Bookstore应用程序的内容来展示JSTL:

·  用JSTL核心标签替换Struts逻辑标签。

·  用消息格式标签替换访问消息储存的scriptlet。

·  用通过JSTL SQL标签对数据库的直接调用替换JavaBean组件数据库helper。对于大多数应用程序来说,最好将对数据库的调用封装到bean中。JSTL包含SQL标签,在创建已有原型的应用程序并有可能减少创建bean的开销的情况下可以使用这个标签。

Duke's Bookstore应用程序的源代码在解开教程压缩包时创建的<JWSDP_HOME>/docs/tutorial/examples/web/bookstore4目录中(见运行示例)。

要编译、安装和运行这个例子:

1.     在终端窗口,进入 <JWSDP_HOME>/docs/tutorial/examples/web/bookstore4.

2.     运行ant build。Build目标会进行所有需要的编译并将文件拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore4/build 目录中。

3.     确保已经启动Tomcat。

4.     运行ant install。install目标通知Tomcat新的内容已经可用。

5.     如果还没有做的话,启动PointBase数据库服务器并填入数据 (见Web应用程序访问数据库)。

6.     打开书店URL http://localhost:8080/bookstore4/enter.

有关诊断常问题的帮助见常见问题及其解决方法故障排除

使用 JSTL

JSTL包括很多种不同的标签,可应用到不同功能的领域中。因此,通过多个标签库描述符(TLD)明确表明它所覆盖的功能领域,并给每一领域它自己的命名空间。17-1总结了这些功能领域,以及在本章和Duke's Bookstore应用程序中使用的逻辑TLD名和前缀。

17-1 JSTL 标签  

领域

功能

标签

TLD

前缀

核心

支持表达式语言

catch

out

remove

set

/jstl-c

c

流程控制

choose

  when

  otherwise

forEach

forTokens

if

URL管理

import

  param
redirect

  param

url

  param

XML

核心

out

parse

set

/jstl-x

x

流程控制

choose
  when
  otherwise

forEach

if

转换

transform

  param

I18n

区域

setLocale

/jstl-fmt

fmt

编排消息格式

bundle

message

  param

setBundle

编排数字和日期格式

formatNumber

formatDate

parseDate

parseNumber

setTimeZone

timeZone

数据库

 

setDataSource

/jstl-sql

sql

SQL

query

  dateParam

  param

transaction

update
  dateParam

  param

例如,要在JSP页面中使用JSTL核心标签,用引用TLD的taglib指令声明库:

<%@ taglib uri="/jstl-core" prefix="c" %>

JSTL标签库有两种版本(见孪生库)。JSTL-EL库的TLD命名为prefix.tld。JSTL-RT库的TLD命名为prefix-rt.tld。由于在本章讨论的例子使用逻辑TLD名,所以我们用Web应用部署描述符中的taglib元素将逻辑名映射为实际TLD位置。下面是将核心库逻辑TLD名/jstl-c映射到其位置/WEB-INF/c.tld的项:

<taglib>
 <taglib-uri>/jstl-c</taglib-uri>
 <taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>

在Java WSDP中,JSTL TLD储存在<JWSDP_HOME>/jstl-1.0.3/tld中。在构建Duke's Bookstore应用程序时,这些TLD自动拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore4/build/WEB-INF中。

还可以在taglib指令中用绝对URI引用TLD:

·  核心: http://java.sun.com/jstl/core

·  XML: http://java.sun.com/jstl/xml

·  国际化: http://java.sun.com/jstl/fmt

·  SQL: http://java.sun.com/jstl/sql

在使用绝对URI时,不必在web.xml中添加taglib元素,JSP容器会自动定位在JSTL库实现中的TLD。

除了声明标签库,还需要使JSTL API及其实现对于Web应用程序是可用的。它们是以<JWSDP_HOME>/jstl-1.0.3中的jstl.jar和<JWSDP_HOME>/jstl-1.0.3/standard中的standard.jar文档形式发布的。在构建Duke's Bookstore应用程序时,这些库会自动拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore4/build/WEB-INF/lib中。

表达式语言支持

JSTL的一个重要功能就是对表达式语言(EL)的支持。表达式语言结合JSTL标签,使得访问应用程序数据和以简单方式操纵它变得容易了,不需要使用scriptlet或者请求时表达式。当前,页面编写者必须使用表达式<%= aName %>访问系统的值或者用户定义的JavaBean组件。如:

<x:aTag att="<%= pageContext.getAttribute("aName") %>">  

对于嵌入式bean属性就更复杂了:

<%= aName.getFoo().getBar() %>

这使得页面的编写比它需要的更复杂。

表达式语言使页面编写者可以用简单的语法访问对象,如对于简单的变量

<x:atag att="${aName}">  

或者对于嵌入的属性

<x:aTag att="${aName.foo.bar}">  

JSTL表达式语言将JSP scoped 属性提升为业务逻辑与JSP页面交换信息的标准方式。例如,为这个条件标签的test属性提供一个比较名为cart的会话作用域属性中的项目数是否为0的表达式:

<c:if test="${sessionScope.cart.numberOfItems > 0}">   ... </c:if>

JSP规范的下一个版本将会对所有自定义标签库的表达式语言进行标准化。这一版本的JSTL包括这一表达式语言的一个快照。

孪生库

JSTL标签库有两个版本,它们只是在对属性值使用运行时表达式的方式上有所不同。

在JSTL-RT标签库,表达式是在页面的脚本语言中指定的。这正是当前标签库的工作方式。

在JSTL-EL标签库中,表达式是在JSTL表达式语言中指定的。表达式是以EL的语法所写的一个String文字。

在使用EL标签库时,不能为一个属性的值传递脚本语言表达式。这个规则使它可以在转换时验证表达式的语法。

JSTL 表达式语言

JSTL表达式语言负责处理表达式和文字。表达式由${ }字符所包围。例如:

<c:if test="${bean1.a < 3}" />

所有不以${开始的值都认为是文字,用所预期的类型的PropertyEditor将文字解析为预期类型:

<c:if test="true" />

包含字符的文字值必须进行如下转义:

<mytags:example attr1="an expression is ${'${'}true}" />

属性

可以通过名称访问属性,也可以加上作用域。属性的属性是用.操作符访问的,并可任意嵌套。

EL统一了对.和[ ]操作符的处理。因此,expr-a.expr-b等于expr-a[expr-b]。要判断expr-a[expr-b],需将expr-a按value-a判断,将expr-b按value-b判断。

·  如果value-a是一个Map,返回value-a.get(value-b)。

·  如果value-a是一个List或者数组,强制value-b为int并相应并返回value-a.get(value-b)或者Array.get(value-a, value-b)。

·  如果value-a是JavaBean对象,强制value-b为String。如果value-b是value-a的可读属性,则返回getter调用的结果。

EL通过将标识作为属性查询、根据PageContext.findAttribute(String)的行为对标识进行判断。例如,${product}会查询名为product的属性,搜索页面、请求、会话和应用程序作用域,并返回其值。如果没有找到属性,则返回null。注意与一个在下面一节中描述的隐式对象相匹配的标识会返回该隐式对象而不是属性值。

隐式对象

JSTL 表达式语言定义了一组隐式对象:

·  pageContext: PageContext对象

·  pageScope :一个Map,它将页面作用域属性名映射为它们的值

·  requestScope:一个Map,它将请求作用域的属性名映射为它们的值

·  sessionScope :一个Map,它将会话作用域的属性名映射为它们的值

·  applicationScope:一个Map,它将应用程序作用域的属性值映射为它们的值

·  param:一个Map,它将参数名映射为单个String参数值(通过调用ServletRequest.getParameter(String))

·  paramValues: 一个Map,它将参数名映射为该参数的所有值的String[ ] (通过调用获得ServletRequest.getParameterValues(String) 获得)

·  header :一个Map,它将头名映射到单个String头值(通过调用ServletRequest.getheader(String)获得)

·  headerValues: 一个Map,它将头名映射到该参数的所有值的String[ ] (通过调用ServletRequest.getHeaders(String) 获得)

·  cookie: 一个Map,它将cookie名映射为单个Cookie (通过调用HttpServletRequest.getCookie(String) 获得)

·  initParam : 一个Map,它将参数名映射到单个String值 (通过调用ServletRequest.getInitParameter(String) 获得)

当表达式通过名字引用其中一个对象时,返回的是相应的对象而不是相应的属性。例如:${pageContext}返回PageContext对象,即使有一个现有的包含一些其他值的pageContext属性。表17-2显示了使用这种隐式对象的一些例子。

17-2 JSTL 表达式例子

表达式

结果

${pageContext.request.contextPath}

上下文路径(从HttpServletRequest获得)

${sessionScope.cart.numberOfItems}

名为cart的会话作用域的numberOfItems属性

${param["mycom.productId"]}

mycom.productId参数的String 值

Literals

·  Boolean: true和false

·  Long: 与在Java中的一样

·  Floating point: 与在Java中一样

·  String: 带单引号和双引号。"转义为\",' 转义为\',而\ 转义为\\。

·  Null: null

操作符

EL提供了下列操作符:

·  算术:+、-、*、/和div、%和mod、-

·  逻辑: and、&&、or、||、not、!

·  关系: ==、eq、!=、ne、<、lt、>、gt、<=、ge、>=、le。可以是针对其他值,也可能针对布尔、字符串、整数或者浮点文字进行比较。

·  空: empty 操作符是一个前缀操作符,可以用来决定一个值是否为null或者空。

有关这些操作符的优先级和效果参见JSTL 1.0规范

标签协作

标签通常与它们的环境以隐式或者显式的方式协作。隐式协作是通过让上级标签公开定义好的接口、使嵌套的标签无缝地与上级标签协作来实现的。JSTL迭代器标签支持这种模式的协作。

显示协作在标签向环境公开信息时出现。传统上,这是通过公开一个脚本变量实现的(由JSP作用域的属性提供实际对象)。因为JSTL有一个表达式语言,因此不太需要脚本变量。所以JSTL标签(包括EL和RT版本)只是以JSP作用域属性公开信息,不使用脚本变量。下面的JSTL规范对于所有导出有关标签信息的标签属性使用名字var。例如,forEach标签以下面的方式公开购物车中正在迭代的当前项目

<c:forEach var="item" items="${sessionScope.cart.items}">
  ...
</c:forEach>

选用名字var以突出公开的作用域变量不是脚本变量(一般情况下对于属性应命名为id)。

对于标签公开多种情息的情况,名字var用于公开的主要信息,对于公开的其他二级信息选用其他合适的名字。例如,forEach标签通过status属性公开迭代状态信息。

核心标签

核心标签包括那些与表达式、流程控制和一种通用的访问那些基于URL的、其内容可以包含在JSP页面或者在JSP页面中被处理的资源的方式相关的标签。

17-3 核心标签  

领域

功能

标签

TLD

前缀

核心

表达式语言支持

catch

out

remove

set

/jstl-c

c

流程控制

choose

  when

  otherwise

forEach

forTokens

if

URL管理

import

  param

redirect

  param

url

  param

表达式标签

out标签判断一个表达式并将判断结果输出为当前JspWriter对象。它对应于JSP语法<%= expression %>。例如,showcart.jsp像下面这样显示在购物车上的项目:

<c:out value="${sessionScope.cart.numberOfItems}"/>

set标签设置在任何JSP作用域(页面、请求、会话、应用程序)中的属性的值。如果该属性不存在,则创建它。

可以用属性值设置JSP作用域属性:

<c:set var="foo" scope="session" value="..."/>  

也可以用标签正文设置JSP作用域属性:

<c:set var="foo">
  ...
</c:set>

例如,下面用名为Remove的请求参数设置名为bookID的页面作用域属性:

<c:set var="bookId" value="${param.Remove}"/>

如果使用RT版本的库,那么语句将为:

<c_rt:set var="bookId"
  value="<%= request.getParameter("Remove") %>" />

要删除一个作用域属性,使用remove标签。调用书店JSP页面receipt.jsp时,购货会话就结束了,所以像下面这样删除会话属性cart:

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

JSTL表达式语言减少了对脚本的需要。不过,页面编写者仍然会遇到在页面的脚本语言中一些非JSTL标签属性必须用表达式指定的情况。用标准JSP元素jsp:useBean声明可以在脚本表达式或者scriptlet中使用的脚本变量。例如,showcart.jsp使用scriptlet从购物车上删除一本书。这本要删除的书的ID作为请求参数传递。请求参数的值首先设置为页面属性(以便在后被标签JSTL sql:query使用),然后声明为脚本变量并传递给cart.remove方法:

<c:set var="bookId" value="${param.Remove}"/>
<jsp:useBean id="bookId" type="java.lang.String" />
<% cart.remove(bookId); %>
<sql:query var="books"
  dataSource="${applicationScope.bookDS}">
  select * from PUBLIC.books where id = ?
  <sql:param value="${bookId}" />
</sql:query>

catch标签为JSP错误页面机制提供了一种补充。它让页面编写者能够从可以控制的错误条件中正常恢复。页面上最重要的行动应当封装在catch中,这样它们的异常就会传播到错误页面。页面中次要的行动应当包装到catch中,这样它们就不会调用错误页面机制。

抛出的异常储存在由var标识的作用域变量中,它总是有页面作用域。如果没有发生异常,那么如果有由var标识的作用域变量的话,就将它删除。如果没有var,那么异常就仅仅被捕捉而不保存。