Java Server Pages (JSP) 标准标记库 (JSTL)的版本的发布对于JSP/servlet开发者来说是一个重大的进展。有了一个表达语言(EL)和一组四种强大的、易于学习的标准标记库,JSTL极有可能不久后成为实现动态的、基于Java的站点的主要方法。
1996年对JSP servlet的介绍使得Java成为动态网页开发的合理的选择。随后出现的Java服务器页(JSP)是走向快速的、可维护的Java网页实现过程的合理进化的一步,紧接着就出现了JSP标记的支持。但是2002中期的JSTL(JSP标准标记库)的发布才是进一步加速和简化开发过程最大的一步。
在本文中,我解释了JSTL的功能,也涵盖了你启动JSTL所需的每件事情。假设你有了对Java、JSP、XML和安装一个Web容器的基本理解。如果你对这些主题感到陌生,你可以在Resources(http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl.html?#resources#resources)上浏览一下背景参考。在下面的阐述中,假定你有XML和SQL的知识。
安装JSTL支持
对于我们的JSTL安装例子来说,我们使用Tomcat 4.1(尽管任意支持Servlet 2.3和JSP1.2说明的servlet容器都应该能工作)。首先,下载Tomcat 4.1并且按指示进行安装(注意:JSTL需要一个JSP1.2 Web容器)。
用tomcat4 start程序启动Tomcat,并且运行index.html页来确保Tomcat是激活的、且运行良好。
接下来,安装JSTL支持,你可以从Jakarta站点下载JSTL支持,(http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl.html?#resources#resources)并按照下面步骤:
1. 从Jakarta站点下载JSTL检索、解压/脱档该文件。(http://www.javaworld.com/javaworld/jw-02-2003/jw-0228-jstl.html?#resources)
2. 复制你已经解压为common/lib的jar文件到你的Tomcat安装路径中(尽管我们的项目不需要所有的jar文件);这使得JSTL文件适用于你的任意Web应用。
3. 对于任意你想使用JSTL的Web应用来说,复制.tld文件到你的Web应用的WEB-INF目录下。
4. 对于你的JSTL的Web应用,编辑web.xml并添加下列输入:
<taglib>
<taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
<taglib-location>/WEB-INF/fmt.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/sql</taglib-uri>
<taglib-location>/WEB-INF/sql.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/x</taglib-uri>
<taglib-location>/WEB-INF/x.tld</taglib-location>
</taglib>
这些输入让你的Web应用可使用JSTL标记库的表达语言(EL)版本。这些输入的位置有关系!如果你不肯定将他们输入到何处,关于web.xml选项和顺序的权威性指导在文档类型定义(DTD)中定义过:http://java.sun.com/j2ee/dtds/web-app_2_2.dtd。
5. 当你创建了一个使用JSTL的JSP页时,将他输入到你的Web应用的主目录下,就像其它JSP和HTML页一样。你可以为该页任意取名,但是它必须有一个.jsp的扩展名。
基础原理
首先,所有的JSTL也是JSP页,JSTL只是JSP功能的一个超集。
同样,所有的JSTL标记是有效的XML。这意味着:如果你将JSTL标记之外的内容当作临时文本(通常是HTML)时,剩下的JSTL标记必须当作合法的XML来解释。这里有一些隐含的规则,它要求大部分XML语法都是正确的。
JSTL提供一组四个标准标记库(core核心、internationalization/format国际标准化/格式、XML和SQL)并支持EL。JSTL和EL的基本设计目的是简化网页开发和实现。
在本文中,我们遵循JSTL说明书的命名规则,并且把JSTL标记当作动作。一个JSTL标记对应于某个动作;调用动作明确的提醒我们:他们添加动态行为到一个另外的静态页上。
JSTL标记库有两个版本:一个用你以前使用的方法将JSP表达式插入,如<%= . . . %>,另一个使用一个JSTL EL.我会在后面进一步讨论在JSTL中的EL支持。
EL支持
要理解当前JSTL对EL支持的状态,让我们检查相关的说明书是怎样处理的。Java说明书请求(JSR)专家组成员决定:由于某种原因,EL规范说明应该是JSP说明书的一部份,而不是JSTL说明书的一部分。EL完整的说明书成为JSP2.0的一部分。因为JSTL1.0在JSP1.3之前已经完成,JSTL作者不得不对用于JSTL1.0的EL支持实现做一个合理的猜测(结果可能是相当好的)。一个JSTL维护版本将与JSP1.3说明书保持一致,并为了使EL适合JSP1.3说明书,JSTL维护版本做了一些修正。
本文描述的EL概要可能在即将发布的JSTL版本中有一点改动,但是任何改动都是微小的。
EL用于简单的语法定义了一套功能强大的语言,它很容易学习。它融合着JavaScript语言和Perl语言较好的部分的一些风格。EL表达式,集成了来自四种标准标记库的JSTL标记,提供了一个巨大的、灵活的属性集。
所有的EL表达式都被${}括起来。JSTL中的表达式总是JSTL标记中的属性值的一部分。表达式可以是属性的一部分或者合并和嵌入到一个字符串直接量中。JSTL属性也可以包含简单的字符串直接量。在接下来的JSTL中,我们在一个来自核心库的c:out动作中说明每种情况,这个核心库将它的value属性值送到JSP输出上:
<c:out value="${anExpression}"/>
<c:out value="literalText${anExpression}${anotherExpression}"/>
<c:out value="literalText"/>
EL也定义了一套规则用于强制表达式中的值转换为上下文相对应的类型。我们在这里不详细讨论这些规则;然而,它的方法与用Perl语言定义的方式非常相似(就像Perl语言中的做法一样,该方法理所当然的运行良好,但是,偶尔,也会出现可能不是你十分想要的那种结果,但你可以接受)。
EL为访问对象属性、集合元素、一组隐藏对象以及使用相关的、逻辑的和算术的操作符提供了支持。对于索引的属性来说,包括数组和java.util.List类在内,元素可用下列语法访问:
${alist[4]}
${aList[someVariable]}
JavaBean属性和java.util.Map元素(它代表一系列名字/值对)都可以使用下列方法的一个访问得到。在以下的开头两个表达式里,我们可以访问到一个用JavaBean语言命名为aProperty的属性或者用关键字aProperty访问到一个Map实体。在第三个表达式中(注意:我已经省略了引号),我们用保存在变量aVariableContainingPropertyName中的一个名字访问了在anObject之内的一个元素:
${anObject.aProperty}
${anObject["aPropertyName"]}
${anObject[aVariableContainingPropertyName]}
在EL中定义了许多隐藏变量:
pageContext:用于该网页的pageContext对象
pageScope, requestScope, sessionScope, 和 applicationScope:这些是映射这些范围的每一个变量到值上的Map集。
param 和 paramValues:用页访问传递的参数,与在JSP中一样
header 和 headerValues:用页请求传递的头,与在JSP中一样
cookie:Map映射cookie程序到特定的cookie对象上
EL定义了全套与你在Java中非常熟悉的那些完全对应的操作符。算法操作符包括+、 -、 *、 / (或 div)、 和% (或 mod)。相关的操作符包括==、!=、<、>、<=、>=,它分别对应于eq、ne、lt、gt、le、和ge。我不想详细阐述这些操作符,因为他们都能自我说明。
JSTL标记库
至此,我已经解释了一些基本原理并且涵盖了EL语法。我要专门讨论四种JSTL标记库。我主要讨论核心库,因为它是你一定要使用的;但是,我也会详细的讨论其余几个,以便你能启动它。
首先,尽管我应该更多地谈论JSTL标记库的两种风格,我上面提到的每个JSTL标记库以两个版本出现:一个支持使用EL的表达式;另一个支持标准JSP表达式。当你引入任何标记库到一个JSP页时,你要定义一个前缀,该前缀指定了与库中标记相对应的命名空间。
四种标准标记库,和他们的JSTL规范定义的前缀常规,列表如下。记住:你可以定义自己的前缀,但是这绝对没有什么好处。
四种标准标记库
库
EL 库前缀
请求时间 (RT) 库前缀
核心
c
c_rt
国际标准化/格式
fmt
fmt_rt
SQL/DB 支持
sql
sql_rt
XML
x
x_rt
为了使用页中的核心标记库( 实际上,你使你的页对标记库的命名空间可见),在你的页首包含下列例子中的指令:
<%@ taglib prefix="c" uri=http://java.sun.com/jstl/core %>
为了使用该标记库的标记,用你在你的包含语句中设计好的前缀,在你的页中给每个标记加上前缀:
<c:out value="${anExpression}"/>
核心数据库
让我们更详细的测试核心数据库,先看看他最通用的功能。
显示/设定值和异常处理
核心库的最基本的标记是c:out标记,它在页中显示一个EL表达式的值。一个使用c:out的表达式通常是这样:
We have <c:out value="${applicationScope.product.inventoryCount}" escapeXml="true" default="0" /> of those items in stock.
上例中,value属性是我们送到页输出的表达式。我也说明了选项escapeXml的属性和默认属性。escapeXml的属性指定了XML字符(<、>、 &、 和 .)是否应该转化为相应的字符实体代码(这样他们可以让这些字符出现在一个HTML页中);默认属性用于EL不能估算出数值或者数值计算出来为空的情况。
注意:当EL支持完全贯穿整个JSP2.0的时候,你不需要使用c:out动作;你可以仅嵌套JSP表达式直接在页中。
另一个普遍使用的核心动作是c:set,它在页中设定一个变量。你可以用两种方法使用c:set动作,第一种方法设定在var属性中定义的变量到在value属性中定义的值上,方法如下:
<c:set var="customerID" value="$param:customerNumber" scope="session" />
上述的选择项scope属性指定了我们要在会话期范围内设定变量customerID;如果范围没有制定,范围默认为页。
c:set另一个强大的用途就是将c:set标记的体的内容赋给一个指定的变量:
<c:set var="cellContents">
<td>
<c:out value="${myCell}"/>
</td>
</c:set>
在上例中,c:set动作定义了名为cellContents(在页的范围内)的变量,该变量拥有在标记的体中定义的内容。在这种情况下,体定义了一个HTML表格单元格元素,计算体中的c:out动作,并且该计算的结果包括在体中的字符串直接量值。
就像你预想的那样,JSTL进行异常处理有点早。在典型的JSP页中,你有两种异常处理的方法:试图/捕捉直接嵌入到页中的小脚本代码中的块;或者用一个JSP errorPage指令。JSP errorPage指令提供一个良好的捕捉所有异常的方法来处理页中任何可能的异常。JSTL用一个c:catch动作提供一个好的可选择的方式。这个c:catch动作提供一个更细粒度的有效方法来处理异常。而且没有嵌入Java代码到页中。一个c:catch动作是这样的:
<c:catch>
<!--. . . some set of nested JSTL tags below which would be hit on an exception-->
</c:catch>
c:catch动作有一个可选的属性,即一个指向抛出异常的变量。
你可能不怎么愿意使用c:remove标记。这个标记有用于变量名和范围的属性,并且可从指定的范围内删除指定的变量。
流程控制
让我们转为讨论JSTL的流程控制和条件标记。如果你已经用任何一种语言使用了条件和流程控制语句,理论上这里没什么新鲜的东西。
c:if动作处理简单条件语句的测试。计算测试属性中的Boolean表达式的值,如果表达式为真的话,计算体的内容。在下面的动作中,我们也说明了备选项var属性。为了以后的使用,var属性保存测试结果在页(如果没有指定其他scope属性的话)中。
<c:if test="${status.totalVisits == 1000000}" var="visits">
You are the millionth visitor to our site! Congratulations!
</c:if>
下面我们展示了用c:choose、 c:when、 和 c:otherwise交换逻辑的JSTL的支持。一组c:when动作可能包括在一个备选的标记内,如果在c:when块中任何表达式计算值为真的话,就不用计算c:choose动作内的测试。如果c:when块中没有一个测试计算值为真的时候:如果出现c:otherwise动作内容时,则计算c:otherwise动作的内容: