第七章 JSTL 1.1

JSTL 全名为JavaServer Pages Standard Tag Library,目前最新的版本为1.1。JSTL是由JCP

(Java Community Process)所指定的标准规格,它主要提供给Java Web 开发人员一个标准通用的

标签函数库。

Web 程序开发人员能够利用JSTL 和EL来开发Web 程序,取代传统直接在页面上嵌入Java程序

(Scripting)的做法,以提高程序可读性、维护性和方便性。

本章中,笔者将详细介绍如何使用JSTL 中各种不同的标签,将依序介绍条件、循环、URL、U18N、

XML、SQL 等标签的用法,让读者对JSTL 有更深层的了解,并且能够学会如何使用JSTL。

7-1 JSTL 1.1 简介

JavaServer Pages Standard Tag Library (1.1 ),它的中文名称为JSP 标准标签函数库。JSTL

是一个标准的已制定好的标签库,可以应用于各种领域,如:基本输入输出、流程控制、循环、XML

文件剖析、数据库查询及国际化和文字格式标准化的应用等。从表7-1 可以知道,JSTL所提供的标

签函数库主要分为五大类:

(1)核心标签库 (Core tag library)

(2)I18N 格式标签库 (I18N-capable formatting tag library)

(3)SQL 标签库 (SQL tag library)

(4)XML 标签库 (XML tag library)

(5)函数标签库 (Functions tag library)

表 7-1

JSTL 前置名称URI 范例

核心标签库c http://java.sun.com/jsp/jstl/core <c:out>

I18N格式标签库fmt http://java.sun.com/jsp/jstl/xml <fmt:formatDate>

SQL 标签库sql http://java.sun.com/jsp/jstl/sql <sql:query>

XML 标签库xml http://java.sun.com/jsp/jstl/fmt <x:forBach>

函数标签库fn http://java.sun.com/jsp/jstl/functions <fn:split>

另外,JSTL 也支持EL(Expression Language)语法,例如:在一个标准的JSP 页面中可能会使

用到如下的写法:

<%= userList.getUser().getPhoneNumber() %>

使用JSTL 搭配传统写法会变成这样:

<c_rt:out value="<%= userList.getUser( ).getPhoneNumber( ) %>" />

使用JSTL 搭配EL,则可以改写成如下的形式:

<c:out value="${userList.user.phoneNumber}" />

虽然对网页设计者来说,假如没有学过Java Script 或者是第一次看到这种写法时,可能会搞

不太懂,但是与Java 语法相比,这应该更加容易学习。

7-1-1 安装使用JSTL 1.1 JSTL

1.1 必须在支持servlet 2.4 且JSP 2.0 以上版本的Container 才可使用。JSTL 主要由apache

组织的Jakarta Project 所实现,因此读者可以到

http://jakarta.apache.org/builds/jakarta-taglibs/releases/standard/ 下载实现好的JSTL

1.1,或者直接使用本__________书光盘中JSTL 1.1,软件名称为:jakarta-taglibs-standard-current.zip。

下载完后解压缩,可以发现文件夹中所包含的内容如图7-1 所示:

图 7-1 jakarta-taglibs-standard-1.1.0-B1 的目录结构

将 lib 中的jstl.jar、standard.jar 复制到Tomcat的WEB-INF\lib 中,然后就可以在JSP 网

页中使用JSTL了。除了复制 .jar 文件外,最好也把tld 文件的目录也复制到WEB-INF 中,以便日

后使用。

注意

lib 目录下,除了jstl.jar 和standard.jar之外,还有old-dependencies目录,这目录里面

的东西是让之前JSTL 1.0 的程序也能够在JSTL 1.1 环境下使用。tld 目录下有许多TLD 文件,其

中大部分都是JSTL 1.0 的TLD 文件,例如:c-1_0.tld 和c-1_0-rt.tld。

下面写一个测试用的范例程序HelloJSTL.jsp,程序主要是显示浏览器的版本和欢迎的字符串。

■ HelloJSTL.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>测试你的第一个使用到JSTL 的网页</title>

</head>

<body>

<c:out value="欢迎测试你的第一个使用到JSTL 的网页"/>

</br>你使用的浏览器是:</br>

<c:out value="${header['User-Agent']}"/>

<c:set var="a" value="David O'Davies" />

<c:out value="David O'Davies" escapeXml="true"/>

</body>

</html>

在HelloJSTL.jsp 的范例里,笔者用到核心标签库(Core)中的标准输出功能和EL 的header

隐含对象。若要在JSP 网页中使用JSTL 时,一定要先做下面这行声明:

< %@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

这段声明表示我将使用JSTL 的核心标签库。一般而言,核心标签库的前置名称(prefix)都为

c,当然你也可以自行设定。不过uri 此时就必须为http://java.sun.com/jsp/jstl/core。

注意

JSTL 1.0 中,核心标签库的uri默认为http://java.sun.com/jstl/core,比JSTL 1.1 少一

个jsp/ 的路径。因为JSTL 1.1 同时支持JSTL 1.0 和1.1,所以假若核心标签库的uri 为

http://java.sun.com/jstl/core,则将会使用到JSTL 1.0 的核心标签库。

接下来使用核心标签库中的out 标签,显示value的值。${header['User-Agent']}表示取得

表头里的User-Agent 的值,即有关用户浏览器的种类。

<c:out value="欢迎测试你的第一个使用到JSTL 的网页" />

<c:out value="${header['User-Agent']}" />

HelloJSTL.jsp 的执行结果如图7-2 所示。

图 7-2 HelloJSTL.jsp 的执行结果

假若读者想要自定义taglib的uri 时,那就必须在web.xml 中加入设定值。例如:假若uri想

要改为http://www.javaworld.com.tw/jstl/core 时,web.xml 就必须加入如下设定:

<web-app>

<jsp-config>

<taglib>

<taglib-uri>http://www.javaworld.com.tw/jstl/core</taglib-uri>

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

</taglib>

</jsp-config>

</web-app>

在上面的设定中,<taglib-uri>主要是设定标签库的URI;而<taglib-location>则是用来设定

标签对应的TLD 文件。因此,使用<%@ taglib %>指令时,可以直接写成如下语句:

<%@ taglib prefix="c" uri="http://www.javaworld.com.tw/jsp/jstl/core" %>

7-1-2 JSTL 1.1 VS. JSTL 1.0

JSTL 1.0 更新至JSTL 1.1 时,有以下几点不同:

(1) EL 原本是定义在JSTL 1.0的,现在EL 已经正式纳入JSP 2.0标准规范中,所以在JSTL 1.1

规范中,已经没有EL 的部分,但是JSTL 依旧能使用EL。

(2) JSTL 1.0 中,又分EL 和RT 两种函数库,到了JSTL 1.1 之后,已经不再分这两种了。以

下说明EL 和RT 的差别:

EL

■ 完全使用Expression Language

■ 简单

■ 建议使用

RT

■ 使用Scriptlet

■ Java 语法

■ 供不想转换且习惯旧表示法的开发者使用

笔者在此强烈建议大家使用EL 来做,简单又方便。

(3) JSTL 1.1 新增函数(functions)标签库,主要提供一些好用的字符串处理函数,例如:

fn:contains、fn:containsIgnoreCase、fn:endsWith、fn:indexOf、fn:join、fn:length、fn:replace、

fn:split、fn:startsWith 和fn:substring 等等。

除了上述三项比较大的改变之外,还包括许多小改变,在此不多加说明,有兴趣的读者可以去

看 JSTL 1.1 附录B“Changes”部分,那里有更详尽的说明。

7-1-3 安装standard-examples

当解压缩jakarta-taglibs-standard-current.zip 后,文件夹内(见图7-1)有一个

standard-examples.war的文件,将它移至Tomcat的webapps后,重新启动Tomcat会发现,在webapps

目录下多了一个standard-examples 的目录。接下来我们打开IE,在URL 位置上输入

http://localhost:8080/ standard-examples,你将会看到图7-3 所示的画面。

这个站台有很多JSTL 的范例,它包括以下几部分:

■ General Purpose Tags

■ Conditional Tags

■ Iterator Tags

■ Import Tags

■ I18N & Formatting Tags

■ XML Tags

■ SQL Tags

■ Functions

■ Tag Library Validators

■ Miscellaneous

图 7-3 standard-examples 站台

这些范例程序几乎涵盖了所有的JSTL 标签函数库,假若读者对哪一个标签的使用有问题,可以

先来找一找这里的范例程序,应该或多或少会有所帮助。

7-2 核心标签库 (Core tag library)

首先介绍的核心标签库(Core)主要有:基本输入输出、流程控制、迭代操作和URL 操作。详细

的分类如表7-2 所示,接下来笔者将为读者一一介绍每个标签的功能。

表 7-2

分类功能分类标签名称

表达式操作

out

set

remove

catch

流程控制

if

choose

when

otherwise

迭代操作

forEach

forTokens

Core

URL 操作

Import

param

url

param

redirect

param

在JSP 中要使用JSTL 中的核心标签库时,必___________须使用<%@ taglib %>指令,并且设定prefix 和

uri 的值,通常设定如下:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

上述的功用在于声明将使用JSTL 的核心标签库。

注意

假若没有上述声明指令,将无法使用JSTL的核心功能,这是读者在使用JSTL 时必须要

小心的地方。

7-2-1 表达式操作

表达式操作分类中包含四个标签:<c:out>、<c:set>、<c:remove>和<c:catch>。接下来将依序

介绍这四个标签的用法。

<c:out>

<c:out>主要用来显示数据的内容,就像是 <%= scripting-language %> 一样,例如:

Hello ! <c:out value="${username}" />

语法

语法1:没有本体(body)内容

<c:out value="value" [escapeXml="{true|false}"] [default="defaultValue"] />

语法2:有本体内容

<c:out value="value" [escapeXml="{true|false}"]>

default value

</c:out>

属性

名称 说明EL 类型必须默认值

value 需要显示出来的值YObject Object 是无

default 如果value 的值为null,则显示default 的值YObject Object 否无

escapeXml 是否转换特殊字符,如:<转换成&lt; Yboolean boolean 否 true

注意

表格中的EL字段,表示此属性的值是否可以为EL 表达式,例如:Y表示 attribute = "${表达式}"

为符合语法的,N 则反之。

Null 和错误处理

· 假若 value为null,会显示default 的值;假若没有设定default的值,则会显示一个空

的字符串。

说明

一般来说,<c:out>默认会将 <、>、’、” 和 & 转换为 &lt;、&gt;、&#039;、&#034; 和 &amp;。

假若不想转换时,只需要设定<c:out>的escapeXml 属性为fasle 就可以了(见表7-3)

表 7-3

字符Entity

< &lt;

> &gt;

’ &#039;

” &#034;

& &amp;

范例

<c:out value="Hello JSP 2.0 !! " />

<c:out value="${ 3 + 5 }" />

<c:out value="${ param.data }" default="No Data" />

<c:out value="<p>有特殊字符</p>" />

<c:out value="<p>有特殊字符</p>" escapeXml="false" />

1.在网页上显示 Hello JSP 2.0 !! ;

2.在网页上显示 8;

3.在网页上显示由窗体传送过来的data 参数之值,假若没有data 参数,或data 参数的值为

null 时,则网页上会显示No Data;

4.在网页上显示“<p>有特殊字符</p>”;

5.在网页上显示“有特殊字符”。

<c:set>

<c:set>主要用来将变量储存至JSP 范围中或是JavaBean 的属性中。

语法

语法1:将 value 的值储存至范围为scope 的 varName 变量之中

<c:set value="value" var="varName" [scope="{ page|request|session|application }"]/>

语法2:将本体内容的数据储存至范围为scope 的 varName 变量之中

<c:set var="varName" [scope="{ page|request|session|application }"]>

… 本体内容

</c:set>

语法3:

将 value 的值储存至 target 对象的属性___________中

< c:set value="value" target="target" property="propertyName" />

语法4:

将本体内容的数据储存至 target 对象的属性中

<c:set target="target" property="propertyName">

… 本体内容

</c:set>

属性

名称 说明EL 类型必须默认值

value 要被储存的值Y Object 否无

var 欲存入的变量名称N String 否无

scope var 变量的JSP 范围N String 否page

target 为一JavaBean 或java.util.Map 对象Y Object 否无

property 指定target 对象的属性Y String 否无

Null 和错误处理

语法3 和语法4 会产生异常错误,有以下两种情况:

☆ target 为null

☆ target 不是java.util.Map 或JavaBean 对象

假若 value 为null 时:将由储存变量改为移除变量

☆ 语法1:由var 和scope 所定义的变量,将被移除

□ 若 scope 已指定时,则PageContext.removeAttribute(varName, scope)

□ 若 scope 未指定时,则PageContext.removeAttribute(varName)

☆ 语法3:

□ 假若 target 为Map 时,则Map.remove(property)

□ 假若 target 为JavaBean 时,property 指定的属性为null

说明

使用<c:set>时,var 主要用来存放表达式的结果;scope 则是用来设定储存的范围,例如:假

若scope="session",则将会把数据储存在session中。如果<c:set>中没有指定scope时,则它会

默认存在Page 范围里。

注意

var 和scope 这两个属性不能使用表达式来表示,例如:我们不能写成

scope="${ourScope}"或者是var="${username}"。

我们考虑下列的写法:

<c:set var="number" scope="session" value="${1 + 1}"/>

把1+1的结果2储存到number变量中。如果<c:set>没有value属性,此时value之值在<c:set>

和</c:set>之间,本体内容看下面的范例:

<c:set var="number" scope="session">

<c:out value="${1+1}" />

</c:set>

上面的 <c:out value="${1+1}" /> 部分可以改写成2 或是 <%=1+1%> ,结果都会一样,也就

是说,<c:set>是把本体(body)运算后的结果来当做value的值。另外,<c:set>会把body 中最开头

和结尾的空白部分去掉。如:

<c:set var="number" scope="session">

_____________1 + 1

</c:set>

则number 中储存的值为1 + 1 而不是 1 + 1。

范例

<c:set var="number" scope="request" value="${1 + 1}" />

<c:set var="number" scope="session" />

${3 + 5}

</c:set>

<c:set var="number" scope="request" value="${ param.number }" />

<c:set target="User" property="name" value="${ param.Username}" />

1.将2 存入Request 范围的number 变量中;

2.将8 存入Session 范围的number 变量中;

3.假若 ${param.number}为null 时,则移除Request 范围的number 变量;若${param.number}

不为null 时,则将 ${param.number}的值存入Request 范围的number 变量中;

4.假若 ${param.Username}为null 时,则设定User(JavaBean)的name 属性为null;若不为

null 时,则将 ${param.Username}的值存入User(JavaBean)的name 属性(setter 机制)。

注意

上述范例的3.中,假若 ${param.number}为null时,则表示移除Request范围的number变量。

● <c:remove>

<c:remove>主要用来移除变量。

语法

<c:remove var="varName" [scope="{ page|request|session|application }"] />

属性

名称 说明EL 类型必须默认值

var 欲移除的变量名称N String 是无

scope var 变量的JSP 范围N String 否page

说明

<c:remove>必须要有var 属性,即要被移除的属性名称,scope 则可有可无,例如:

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

将number 变量从Session 范围中移除。若我们不设定scope,则<c:remove>将会从Page、

Request、Session 及Application 中顺序寻找是否存在名称为number 的数据,若能找到时,

则将它移除掉,反之则不会做任何的事情。

范例

笔者在这里写一个使用到<c:set>和<c:remove>的范例,能让读者可以更快地了解如何使用

它们,此范例的名称为Core_set_remove.jsp。

■ Core_set_remove.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_set_remove.jsp</title>

</head>

<body>

<h2><c:out value="<c:set>和<c:remove> 的用法" /></h2>

<c:set scope="page" var="number">

<c:out value="${1+1}"/>

</c:set>

<c:set scope="request" var="number">

<%= 3 %>

</c:set>

<c:set scope="session" var="number">

4

</c:set>

初始设置

<table border="1" width="30%">

<tr>

<th>pageScope.number</th>

<td><c:out value="${pageScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>requestScope.number</th>

<td><c:out value="${requestScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>sessionScope.number</th>

<td><c:out value="${sessionScope.number}" default="No Data" /></td>

</tr>

</table></br>

<c:out value='<c:remove var="number" scope="page" />之后'/>

<c:remove var="number" scope="page" />

<table border="1" width="30%">

<tr>

<th>pageScope.number</th>

<td><c:out value="${pageScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>requestScope.number</th>

<td><c:out value="${requestScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>sessionScope.number</th>

<td><c:out value="${sessionScope.number}" default="No Data" /></td>

</tr>

</table></br>

<c:out value='<c:remove var="number" />之后'/>

<c:remove var="number" />

<table border="1" width="30%">

<tr>

<th>pageScope.number</th>

<td><c:out value="${pageScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>requestScope.number</th>

<td><c:out value="${requestScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>sessionScope.number</th>

<td><c:out value="${sessionScope.number}" default="No Data" /></td>

</tr>

</table>

</body>

</html>

笔者一开始各在Page、Request和Session三个属性范围中储存名称为number 的变量。然

后先使用<c:remove var="number" scope="page" />把Page 中的number 变量移除,最后再使

用<c:remove var="number" />把所有属性范围中number 的变量移除。Core_set_remove.jsp

的执行结果如图7-4 所示:

图 7-4 Core_set_remove.jsp 的执行结果

● <c:catch>

<c:catch>主要用来处理产生错误的异常状况,并且将错误信息储存起来。

语法

<c:catch [var="varName"] >

… 欲抓取错误的部分

</c:catch>

属性

名称 说明EL 类型必须默认值

var 用来储存错误信息的变量N String 否无

说明

<c:catch>主要将可能发生错误的部分放在<c:catch>和</c:catch>之间。如果真的发生错

误,可以将错误信息储存至varName 变量中,例如:

<c:catch var="message">

: //可能发生错误的部分

</c:catch>

另外,当错误发生在<c:catch>和</c:catch>之间时,则只有<c:catch>和</c:catch>之间的程序

会被中止忽略,但整个网页不会被中止。

范例

笔者写一个简单的范例,文件名为Core_catch.jsp,来让大家看一下<c:catch>的使用方式。

■ Core_catch.jsp

<%@ page contentType="text/html;charset=GB2312 " %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_catch.jsp</title>

</head>

<body>

<h2><c:out value="<c:catch> 的用法" /></h2>

<c:catch var="error_Message">

<%

String eFormat = "not number";

int i = Integer.parseInt(eFormat);

%>

</c:catch>

${error_Message}

</body>

</html>

笔者将一个字符串转成数字,如果字符串可以转为整数,则不会发生错误。但是这里笔者故意

传入一个不能转成数字的字符串,让<c:catch>之间产生错误。当错误发生时,它会自动将错误存到

error_Message 变量之中,最后再用<c:out>把错误信息显示出来,执行结果如图7-5 所示。

图 7-5 Core_catch.jsp 的执行结果

可以发现到网页确实显示格式错误的信息。如果我们不使用<c:catch>,而把范例中的<c:catch>

和</c:catch>拿掉,结果如图7-6 所示。

图 7-6 Core_catch.jsp 没有<c:catch>和</c:catch>的执行结果

7-2-2 流程控制

流程控制分类中包含四个标签:<c:if>、<c:choose>、<c:when>和<c:otherwise>,笔者依此顺

序依次说明这四个标签的使用。

● <c:if>

<c:if>的用途就和我们一般在程序中用的if 一样。

语法

语法1:没有本体内容(body)

<c:if test="testCondition" var="varName"

[scope="{page|request|session|application}"]/>

语法2:有本体内容

<c:if test="testCondition" [var="varName"]

[scope="{page|request|session|application}"]>

具体内容

</c:if>

属性

名称 说明EL 类型必须默认值

test

如果表达式的结果为true,则执行本体内容,false

则相反

Y boolean 是 无

var 用来储存test 运算后的结果,即true 或false N String 否 无

scope var 变量的JSP 范围N String 否 page

说明

<c:if> 标签必须要有test 属性,当test 中的表达式结果为true 时,则会执行本体内容;如

果为false,则不会执行。例如:${param.username = = 'admin'},如果param.username 等于admin

时,结果为true;若它的内容不等于admin 时,则为false。

接下来看下列的范例:

<c:if test="${param.username = = 'admin' }">

ADMIN 您好!! //body 部分

</c:if>

如果名称等于admin,则会显示"ADMIN您好!! "的动作,如果相反,则不会执行<c:if>的body

部分,所以不会显示"ADMIN 您好!! //body 部分"。另外<c:if>的本体内容除了能放纯文字,还可以

放任何JSP 程序代码(Scriptlet)、JSP 标签或者HTML 码。

除了test 属性之外,<c:if>还有另外两个属性var和scope。当我们执行<c:if>的时候,可以

将这次判断后的结果存放到属性var 里;scope 则是设定var 的属性范围。哪些情况才会用到var

和scope 这两个属性呢?例如:当表达式过长时,我们会希望拆开处理,__________或是之后还须使用此结果

时,也可以用它先将结果暂时保留,以便日后使用。

范例

笔者写了一个简单的范例,名称为Core_if.jsp。

■ Core_if.jsp

<%@ page contentType="text/html;charset=GB2312 " %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<html>

<head>

<title>CH7 - Core_if.jsp</title>

</head>

<body>

<h2><c:out value="<c:if> 的用法" /></h2>

<c:if test="${param.username == 'Admin'}" var="condition" scope="page">

您好Admin 先生

</c:if></br>

执行结果为: ${condition}

</body>

</html>

笔者在判断用户送来的参数时,如果username 的值等于Admin 时,则会将condition 设为true

并存放于pageScope中,否则存放于condition中,最后再显示结果。因为JSTL会自动找寻condition

所存在的属性范围,因此只须使用${condition},而不用 ${pageScope.condition}。Core_if.jsp

的执行结果如图7-7。

注意

执行本范例时,请在Core_if.jsp 后加上?username=Admin。

图 7-7 Core_if.jsp的执行结果

● <c:choose>

<c:choose>本身只当做 <c:when> 和 <c:otherwise> 的父标签。

语法

<c:choose>

本体内容( <when> 和 <otherwise> )

</c:choose>

属性

限制

<c:choose>的本体内容只能有:

·空白

·1 或多个 <c:when>

·0 或多个 <c:otherwise>

说明

若使用<c:when>和<c:otherwise>来做流程控制时,两者都必须为<c:choose>的子标签,即:

<c:choose>

<c:when>

</c:when>

<c:otherwise>

</c:otherwise>

</c:choose>

● <c:when>

<c:when> 的用途就和我们一般在程序中用的when 一样。

语法

<c:when test="testCondition" >

本体内容

</c:when>

属性

名称 说明EL 类型必须默认值

test

如果表达式的结果为true,则执行本体内容,false

则相反

Y boolean 是 无

限制

☆ <c:when>必须在<c:choose>和</c:choose>之间

☆ 在同一个<c:choose>中时,<c:when>必须在<c:otherwise>之前

说明

<c:when>必须有test 属性,当test 中的表达式结果为true 时,则会执行本体内容;如果为

false 时,则不会执行。

● <c:otherwise>

在同一个 <c:choose> 中,当所有 <c:when> 的条件都没有成立时,则执行<c:otherwise> 的

本体内容。

语法

<c:otherwise>

本体内容

</c:otherwise>

属性

限制

·<c:otherwise> 必须在 <c:choose> 和 </c:choose>之间

·在同一个 <c:choose> 中时,<c:otherwise> 必须为最后一个标签

说明

在同一个<c:choose> 中,假若所有<c:when> 的test 属性都不为true 时,则执行

<c:otherwise> 的本体内容。

范例

笔者举一个典型的 <c:choose>、<c:when>和<c:otherwise>范例:

<c:choose>

<c:when test="${condition1}">

condition1 为true

</c:when>

<c:when test="${ condition2}">

condition2 为true

</c:when>

<c:otherwise>

condition1 和condition2 都为false

</c:otherwise>

</c:choose>

范例说明:当condition1 为true 时,会显示“condition1为true”;当condition1为false

且condition2为true 时,会显示“condition2为true”,如果两者都为false,则会显示__________“condition1

和condition2 都为false”。

注意

假若 condition1 和condition2 两者都为true 时,此时只会显示"condition1 为true",这是因为

在同一个<c:choose>下,当有好几个<c:when>都符合条件时,只能有一个<c:when>成立。

7-2-3 迭代操作

迭代(Iterate)操作主要包含两个标签:<c:forEach>和<c:forTokens>,笔者依此顺序依次说明

这两个标签的使用。

● <c:forEach>

<c:forEach> 为循环控制,它可以将集合(Collection)中的成员循序浏览一遍。运作方式为当

条件符合时,就会持续重复执行<c:forEach>的本体内容。

语法

语法1:迭代一集合对象之所有成员

<c:forEach [var="varName"] items="collection" [varStatus="varStatusName"] [begin="begin"]

[end="end"] [step="step"]>

本体内容

< /c:forEach>

语法2:迭代指定的次数

<c:forEach [var="varName"] [varStatus="varStatusName"] begin="begin" end="end"

[step="step"]>

本体内容

</c:forEach>

属性

名称 说明EL 类型必须默认值

var 用来存放现在指到的成员N String 否无

items 被迭代的集合对象Y

Arrays

Collection

Iterator

Enumeration

Map

String

否无

varStatus 用来存放现在指到的相关成员信息N String 否无

begin 开始的位置Y int 否0

end 结束的位置Y int 否最后一个成员

step 每次迭代的间隔数Y int 否1

限制

·假若有begin 属性时,begin 必须大于等于 0

·假若有end 属性时,必须大于begin

·假若有step 属性时,step 必须大于等于0

Null 和错误处理

·假若items 为null 时,则表示为一空的集合对象

·假若begin 大于或等于items 时,则迭代不运算

说明

如果要循序浏览一个集合对象,并将它的内容显示出来,就必须有items 属性。

范例

下面的范例 Core_forEach.jsp 是将数组中的成员一个个显示出来的:

■ Core_forEach.jsp

<%@ page contentType="text/html;charset=GB2312 " %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> 的用法" /></h2>

<%

String atts[] = new String [5];

atts[0]="hello";

atts[1]="this";

atts[2]="is";

atts[3]="a";

atts[4]="pen";

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item" >

${item}</br>

</c:forEach>

</body>

</html>

在上述范例中,笔者先产生一个字符串数组,然后将此数组atts 储存至Request 的属性范围中,

再用<c:forEach>将它循序浏览一遍。这里items表示被浏览的集合对象,var用来存放指定的集合

对象中成员,最后使用<c:out>将item 的内容显示出来,执行结果如图7-8 所示。

图 7-8 Core_forEach.jsp的执行结果

注意

varName 的范围只存在<c:forEach>的本体中,如果超出了本体,则不能再取得varName 的值。上个

例子中,若${item} 是在</c:forEach>之后执行时,如:

<c:forEach items="${atts}" var="item" >

</c:forEach>

${item}</br>

${item}则不会显示item 的内容。

<c:forEach>除了支持数组之外,还有标准J2SE 的集合类型,例如:ArrayList、List、

LinkedList、Vector、Stack和Set 等等;另外还包括java.util.Map 类的对象,例如:HashMap、

Hashtable、Properties、Provider 和Attributes。

<c:forEach>还有begin、end 和step 这三种属性:begin主要用来设定在集合对象中开始的位

置(注意:第一个位置为0);end 用来设定结束的位置;而step 则是用来设定现在指到的成员和下

一个将被指到成员之间的间隔。我们将之前的范例改成如下:

■ Core_forEach1.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach1.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> begin、end 和step 的用法" /></h2>

<%

String atts[] = new String [5];

atts[0]="hello";

atts[1]="this";

atts[2]="is";

atts[3]="a";

atts[4]="pen";

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item" begin="1" end="4" step="2" >

${item}</br>

</c:forEach>

</body>

</html>

<c:forEach>中指定的集合对象atts 将会从第2 个成员开始到第5 个成员,并且每执行一次循

环都会间隔一个成员浏览。因此结果是只显示atts[1]和atts[3]的内容,如图7-9 所示。

图 7-9 Core_forEach1.jsp的执行结果

为了方便详细介绍begin、end 和step 的不同设定下所产生的结果,笔者将上面的范例改成如

下:

<%

int atts[] = {1,2,3,4,5,6,7,8,9,10};

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item" begin="0" end="9" step="1" >

${item}</br>

</c:forEach>

这里笔者改变begin、end 和step 的值时,在网页上输出结果的变化如表7-4。

表 7-4

begin end step 结果

- - - 1 2 3 4 5 6 7 8 9 10

5 - - 6 7 8 9 10

- 5 - 1 2 3 4 5 6

- - 5 1 6

5 5 - 6

5 5 5 6

0 8 2 1 3 5 7 9

0 8 3 1 4 7

0 8 4 1 5 9

15 20 - 无

20 8 - 空白结果

0 20 – 1 2 3 4 5 6 7 8 9 10

从表7-4 中可以发现:

(1) 当begin 超过end 时将会产生空的结果;

(2) 当begin 虽然小于end 的值,但是当两者都大过容器的大小时,将不会输出任何东西;

(3) 最后如果只有end 的值超过集合对象的大小,则输出就和没有设定end 的情况相同;

(4) <c:forEach>并不只是用来浏览集合对象而已,读者可以从表7-4 中发现,items并不是一

定要有的属性,但是当没有使用items 属性时,就一定要使用begin 和end 这两个属性。下面

为一个简单的范例:

■ Core_forEach2.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach2.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> 循环" /></h2>

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

${item}</br>

</c:forEach>

</body>

</html>

上述范例中,我们并没有执行浏览集合对象,只是设定begin 和end 属性的值,这样它就

变成一个普通的循环。此范例是将循环设定为:从1 开始__________跑到10,总共会重复循环10 次,并

且将数字放到item 的属性当中。Core_forEach2.jsp 的执行结果如图7-10 所示。

图 7-10 Core_forEach2.jsp 的执行结果

当然它也可以搭配step 使用,如果将step 设定为2,结果如图7-11 所示。

图 7-11 当step 设定为2 时的结果

另外,<c:forEach>还提供varStatus 属性,主要用来存放现在指到之成员的相关信息。例如:

我们写成varStatus="s",那么将会把信息存放在名称为s 的属性当中。varStatus属性还有另外四

个属性:index、count、first 和last,它们各自代表的意义如表7-5:

表 7-5

属性类型意义

index number 现在指到成员的索引

count number 总共指到成员的总数

first boolean 现在指到的成员是否为第一个成员

last boolean 现在指到的成员是否为最后一个成员

我们可以使用varStatus 属性取得循环正在浏览之成员的信息,下面为一个简单的范例:

■ Core_forEach3.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach3.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> varStatus 的四种属性" /></h2>

<%

String atts[] = new String [5];

atts[0]="hello";

atts[1]="this";

atts[2]="is";

atts[3]="a";

atts[4]="pen";

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item"varStatus="s">

<h2><c:out value="${item}"/>的四种属性:</h2>

index:${s.index}</br>

count:${s.count}</br>

first:${s.first}</br>

last:${s.last}</br>

</c:forEach>

</body>

</html>

执行结果如图7-12 所示。

图 7-12 Core_forEach3.jsp 的执行结果

● <c:forTokens>

<c:forTokens> 用来浏览一字符串中所有的成员,其成员是由定义符号(delimiters)所分隔的。

语法

<c:forTokens items="stringOfTokens" delims="delimiters" [var="varName"]

[varStatus="varStatusName"] [begin="begin"] [end="end"] [step="step"]>

本体内容

</c:forTokens>

属性

名称 说明EL 类型必须默认值

var 用来存放现在指到的成员N String 否无

items 被迭代的字符串Y String 是无

delims 定义用来分割字符串的字符N String 是无

varStatus 用来存放现在指到的相关成员信息N String 否无

begin 开始的位置Y int 否0

end 结束的位置Y int 否最后一个成员

step 每次迭代的间隔数Y int 否1

限制

·假若有begin 属性时,begin 必须大于等于 0

·假若有end 属性时,必须大于begin

·假若有step 属性时,step 必须大于等于0

Null 和错误处理

·假若items 为null 时,则表示为一空的集合对象

·假若begin 大于或等于items 的大小时,则迭代不运算

说明

<c:forTokens>的begin、end、step、var 和varStatus 用法都和<c:forEach>一样,因此,笔

者在这里就只介绍items 和delims 两个属性:items 的内容必须为字符串;而delims 是用来分割

items 中定义的字符串之字符。

范例

下面为一个典型的<c:forTokens>的范例:

<c:forTokens items="A,B,C,D,E" delims="," var="item" >

${item}

</c:forTokens>

上面范例执行后,将会在网页中输出ABCDE。它会把符号“,”当做分割的标记,拆成5 个部分,

也就是执行循环5 次,但是并没有将A,B,C,D,E 中的“,”显示出来。items 也可以放入EL 的表达

式,如下:

<%

String phoneNumber = "123-456-7899";

request.setAttribute("userPhone", phoneNumber);

%>

<c:forTokens items="${userPhone}" delims="-" var="item" >

${item}

</c:forTokens>

这个范例将会在网页上打印1234567899,也就是把123-456-7899以“-”当做分割标记,将字

符串拆为3 份,每执行一次循环就将浏览的部分放到名称为item 的属性当中。delims 不只指定一

种字符来分割字符串,它还可以一次设定多个分割字符串用的字符。如下面这个范例:

<c:forTokens items="A,B;C-D,E" delims=",;-" var="item" >

${item}

</c:forTokens>

此范例会在网页输出ABCDE,也就是说,delims 可以一次设定所有想当做分割字符串用的字符。

其实用<c:forEach>也能做到分割字符串,写法如下:

<c:forEach items="A,B,C,D,E" var="item" >

${item}

</c:forEach>

上述范例同样也会在网页输出ABCDE。<c:forEach>并没有delims这个属性,因此<c:forEach>

无法设定分割字符串用的字符,而<c:forEach>分割字符串用的字符只有“,”,这和使用

<c:forTokens>,delims 属性设为“,”的结果相同。所以如果使用<c:forTokens>来分割字符串,

功能和弹性上会比使用<c:forEach>来得较大。

7-2-4 URL 操作

JSTL 包含三个与URL 操作有关的标签,它们分别为:<c:import>、<c:redirect>和<c:url>。

它们主要的功能是:用来将其他文件的内容包含起来、网页的导向,还有url 的产生。笔者将依序

介绍这三个标签。

● <c:import>

<c:import> 可以把其他静态或动态文件包含至本身JSP 网页。它和JSP Action 的

<jsp:include>最大的差别在于:<jsp:include>只能包含和自己同一个web application下的文件;

而<c:import>除了能包含和自己同一个web application 的文件外,亦可以包含不同web

application 或者是其他网站的文件。

语法

语法1:

<c:import url="url" [context="context"] [var="varName"]

[scope="{page|request|session|application}"] [charEncoding="charEncoding"]>

本体内容

</c:import>

语法2:

<c:import url="url" [context="context"]

varReader="varReaderName" [charEncoding="charEncoding"]>

本体内容

</c:import>

属性

名称 说明EL 类型必须默认值

url 一文件被包含的地址Y String 是 无

context

相同Container 下,其他web站台必须以“/”

开头

Y String 否 无

var 储存被包含的文件的内容(以String类型存入) N String 否 无

scope var 变量的JSP 范围N String 否 Page

charEncoding 被包含文件之内容的编码格式Y String 否 无

varReader 储存被包含的文件的内容(以Reader类型存入) N String 否 无

Null 和错误处理

·假若url 为null 或空时,会产生JspException

说明

首先<c:import>中必须要有url 属性,它是用来设定被包含网页的地址。它可以为绝对地址或

是相对地址,使用绝对地址的写法如下:

<c:import url="http://java.sun.com" />

<c:import>就会把http://java.sun.com 的内容加到网页中。

另外<c:import>也支持FTP 协议,假设现在有一个FTP站台,地址为ftp.javaworld.com.tw,

它里面有一个文件data.txt,那么可以写成如下方式将其内容显示出来:

<c:import url="ftp://ftp.cse.yzu.edu.tw/data.txt" />

如果是使用相对地址,假设存在一个文件名为Hello.jsp,它和使用<c:import>的网页存在于

同一个webapps 的文件夹时,<c:import>的写法如下:

<c:import url="Hello.jsp" />

如果以“/”开头,那么就表示跳到web 站台的根目录下,以Tomcat 为例,即webapps 目录。

假设一个文件为hello.txt,存在于webapps/examples/images 里,而context 为examples,可以

写成以下方式将hello.txt 文件包含进我们的JSP 页面之中:

<c:import url="images/hello.txt" />

接下来如果要包含在同一个服务器上,但并非同一个web 站台的文件时,就必须加上context

属性。假设此服务器上另外还有一个web 站台,名为others,others 站台底下有一个文件夹为jsp,

且里面有index.html 这个文件,那么就可以写成如下方式将此文件包含进来:

<c:import url="/jsp/index.html" context="/others" />

注意

被包含文件的web 站台必须在server.xml中定义过,且<Context>的crossContext属性值必须

为true,这样一来,others 目录下的文件才可以被其他 web 站台调用。

server.xml 的设定范例如下:

: <Context path="/others" docBase="others" debug="0"

reloadable="true" crossContext="true"/>

除此之外,<c:import>也提供var 和scope 属性。当var 属性存在时,虽然同样会把其他文件

的内容包含进来,但是它并不会输出至网页上,而是以String 的类型储存至varName中。scope则

是设定varName 的范围。储存之后的数据,我们在需要用时,可以将它取出来,代码如下:

<c:import url="/images/hello.txt" var="s" scope="session" />

我们可以把常重复使用的商标、欢迎语句或者是版权声明,用此方法储存起来,想输出在网页

上时,再把它导入进来。假若想要改变文件内容时,可以只改变被包含的文件,不用修改其他网页。

另外,可以在<c:import>的本体内容中使用<c:param>,它的功用主要是:可以将参数传递给被

包含的文件,它有两个属性name 和value,如表7-6 所示:

表 7-6

名称说明EL 类型必须默认值

name 参数名称Y String 是无

value 参数的值Y String 否本体内容

这两个属性都可以使用EL,所以我们写成如下形式:

<c:import url="http://java.sun.com" >

<c:param name="test" value="1234" />

</c:import>

这样的做法等于是包含一个文件,并且所指定的网址会变成如下:

http://java.sun.com?test=1234

范例

下 面为一用到<c:import> 、<c:param> 及属性范围的范例, Core_import.jsp 和

Core_imported.jsp:

■ Core_import.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_import.jsp</title>

</head>

<body>

<h2><c:out value="<c:import> 的用法" /></h2>

<c:set var="input1" value="使用属性范围传到Core_imported.jsp 中" scope="request"/>包含

core_imported.jsp 中<hr/>

<c:import url="Core_imported.jsp"charEncoding="GB2312" >

<c:param name="input2" value="使用<c:param>传到Core_imported.jsp 中"/>

</c:import><hr/>

${output1}

</body>

</html>

程序中,笔者分别使用<c:set>和<c:param>来传递参数。

■ Core_imported.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>

<head>

<title>CH7 - Core_imported.jsp</title>

</head>

<body>

<fmt:requestEncoding value="GB2312" />

<c:set var="output1" value="使用属性范围传到Core_import.jsp 中" scope="request"/>

${input1}</br>

<c:out value="${param.input2}" escapeXml="true" />

</body>

</html>

Core_imported.jsp 是被包含的文件,它会把从Core_import.jsp 传来的参数分别输出到页面

上,必须注意的是input1 参数是使用属性范围来传递的,因此可以直接用${input1}来得到参数,

而input2 则必须使用${param.input2}来得到参数。

此外,笔者还使用<c:set>来传递值给Core_import.jsp,这就是<c:param>无法做到的动作,

<c:param>只能从包含端抛给被包含端,但是在属性范围中,可以让包含端也能得到被包含端传来的

数据。Core_import.jsp 的执行结果如图7-13 所示:

图 7-13 Core_import.jsp 的执行结果

● <c:url>

<c:url>主要用来产生一个URL。

语法

语法1:没有本体内容

<c:url value="value" [context="context"] [var="varName"]

[scope="{page|request|session|application}"] />

语法2:本体内容代表查询字符串(Query String)参数

<c:url value="value" [context="context"] [var="varName"]

[scope="{page|request|session|application}"] >

<c:param> 标签

</c:url>

属性

名称 说明EL 类型必须默认值

value 执行的URL Y String 是无

context 相同Container 下,其他web 站台必须以“/”开头Y String 否无

var 储存被包含文件的内容(以String 类型存入) N String 否无

scope var 变量的JSP 范围N String 否Page

说明

在这里笔者直接使用例子来说明。

<c:url value=" " >

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

</c:url>

读者可以发现<c:url>也可以搭配<c:param>使用,上面执行结果将会产生一个网址为

?param=value,我们更可以搭配HTML 的<a>使用,如下:

<a href="

<c:url value=" " >

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

</c:url>">Java 爱好者</a>

另外<c:url>还有三个属性,分别为context、var 和scope。context属性和之前的<c:import>

相同,可以用来产生一个其他web站台的网址。如果<c:url>有var属性时,则网址会被存到varName

中,而不会直接输出网址。

哪些状况下才会去使用<c:url>?例如:当我们须动态产生网址时,有可能传递的参数不固定,

或者是需要一个网址能连至同服务器的其他web站台之文件,而且<c:url>更可以将产生的网址储存

起来重复使用。另外,在以前我们必须使用相对地址或是绝对地址去取得需要的图文件或文件,现

在我们可以直接利用<c:url>从web 站台的角度来设定需要的图文件或文件的地址,如下:

<img src="<c:url value="/images/code.gif" />" />

如此就会自动产生连到image文件夹里的code.gif的地址,不再须耗费精神计算相对地址,并

且当网域名称改变时,也不用再改变绝对地址。

● <c:redirect>

<c:redirect>可以将客户端的请求从一个JSP 网页导向到其他文件。

语法

语法1:没有本体内容

<c:redirect url="url" [context="context"] />

语法2:本体内容代表查询字符串(Query String)参数

<c:redirect url="url" [context="context"] > <c:param> </c:redirect >

属性

名称 说明EL 类型必须默认值

url 导向的目标地址Y String 是 无

context 相同Container 下,其他web 站台必须以“/”开头Y String 否 无

说明

url 就是设定要被导向到的目标地址,它可以是相对或绝对地址。例如:我们写成如下:

<c:redirect url="" />

那么网页将会自动导向到。另外,我们也可以加上context这个属性,

用来导向至其他 web站台上的文件,例如:导向到/others下的/jsp/index.html时,写法如下:

<c:redirect url="/jsp/index.html" context="/others" />

<c:redirect> 的功能不止可以导向网页,同样它还可以传递参数给目标文件。在这里我们同样

使用<c:param>来设定参数名称和内容。

范例

■ Core_redirect.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_redirect.jsp</title>

</head>

<body>

<h2><c:out value="<c:redirect> 的用法" /></h2>

<c:redirect url="http://java.sun.com">

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

</c:redirect>

<c:out value="不会执行喔!!!" />

</body>

</html>