JSTL

8章 标准标签库(JSTL)

8.1 准备实验环境

本章中所有的例子程序都在名为JSTL的WEB应用程序中运行,使用的Tomcat服务器版本为Tomcat5.5。为了简化代码和突出重点,本章中所有的JSP网页文件的例子程序都省略了<html>、<head>、<title>、<body>等HTML全局架构标签。本章中用于描述标签的语法的各种符号的意义如下:

  1. […]:表示方括号中的内容是可选的;
  2. {option1|option2|option3|…}:表示花括号中用“|”字符分隔的各个选项不能同时出现,只能出现其中的一项;
  3. 如果属性的值字体加粗,表示这个值是对应属性的默认值;

如果属性的值用斜体字表示,表示该属性可以是表达式;如果标签体的内容用斜体字表示,表示标签体的内容可以是JSP代码。

为了本章后面讲解的方便,这里先创建一个名为UserBean的JavaBean程序,如例程8-1所示。

例程 8-1  UserBean.java

 

package org.it315;

public class UserBean

{

    private String userName;

    private String password;

    private String address;

    private int visitCount;

    public void setUserName(String userName)

    {

        this.userName = userName;

    }

    public String getUserName()

    {

        return userName;

    }

    public void setPassword(String password)

    {

        this.password = password;

    }

    public String getPassword()

    {

        return password;

    }

    public void setAddress(String address)

    {

        this.address = address;

    }

    public String getAddress()

    {

        return address;

    }

    public void setVisitCount(int visitCount)

    {

        this.visitCount = visitCount;

    }

    public int getVisitCount()

    {

        return visitCount;

    }

}

 

 

编译这个源程序,将编译后的UserBean.class文件放在<TOMCAT_HOME>\webapps\JSTL\WEB-INF\classes\org\it315目录中。

8.2  JSTL概述

8.2.1  JSTL简介

从JSP 1.1规范开始JSP就支持使用自定义标签,使用自定义标签大大降低了JSP页面的复杂度,同时增强了代码的重用性,因此自定义标签在WEB应用中被广泛使用。许多WEB应用厂商都开发出了自己的一套标签库提供给用户使用,这导致出现了许多功能相同的标签,令网页制作者无所适从,不知道选择哪一家的好。为了解决这个问题,Apache Jakarta小组归纳汇总了那些网页设计人员经常遇到的问题,开发了一套用于解决这些常用问题的自定义标签库,这套标签库被SUN公司定义为标准标签库(The JavaServer Pages Standard Tag Library),简称JSTL。使用JSTL可以解决用户选用不同WEB厂商的自定义标签时的困惑,JSP规范同时也允许WEB容器厂商按JSTL标签的标准提供自己的实现,以获取最佳性能。

由于JSTL是在JSP 1.2规范中定义的,所以JSTL需要运行在支持JSP 1.2及其更高版本的WEB容器上,例如,Tomcat 5.5。

1.JSTL的逻辑组成

笔者在写本章内容时的JSTL规范的最新版本是JSTL 1.1,它是JSP 2.0最新规范中的一个重要组成部分。JSTL 1.1中包含四个标签库和一组EL函数。为方便用户使用,JSP规范中描述了JSTL的各个标签库的URI地址和建议使用的前缀名,如表8.1所示。本章中在使用JSTL标签时,使用的都是这些建议的前缀。

 

表8.1  JSTL

标签库功能描述

标签库的URI

建议前缀

核心标签库

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

c

XML标签库

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

x

国际化/格式化标签库

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

fmt

数据库标签库

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

sql

EL自定义函数

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

fn

 

下面对JSTL的各个标签库进行简单的介绍:

(1)核心标签库中包含了实现WEB应用中的通用操作的标签。例如,用于输出一个变量内容的<c:out>标签、用于条件判断的<c:if>标签、用于迭代循环的<c:forEach>标签。

(2)国际化/格式化标签库中包含实现WEB应用程序的国际化的标签。例如,设置JSP页面的本地信息、设置JSP页面的时区、绑定资源文件,使本地敏感的数据(例如数值、日期等)按照JSP页面中设置的本地格式显示。

(3)数据库标签库中包含用于访问数据库和对数据库中的数据进行操作的标签。例如,从数据源中获得数据库连接、从数据库表中检索数据等。由于在软件分层的开发模型中,JSP页面仅用作表现层,我们一般不在JSP页面中直接操作数据库,而是在业务逻辑层或数据访问层操作数据库,所以,JSTL中提供的这套数据库标签库没有多大的实用价值。

(4)XML标签库中包含对XML文档中的数据进行操作的标签。例如,解析XML文档、输出XML文档中的内容,以及迭代处理XML文档中的元素。因为XML广泛应用于WEB开发,对XML文档的处理非常重要,XML标签库使处理XML文档变得简单方便,这也是JSTL的一个重要特征。 

(5)JSTL中提供的一套EL自定义函数包含了JSP页面制作者经常要用到的字符串操作。例如,提取字符串中的子字符串、获取字符串的长度和处理字符串中的空格等。

2.JSTL的物理组成

完整的JSTL应包含SUN公司提供的jstl.jar 包和WEB容器产商提供的JSTL实现包,以Apache Jakarta 小组提供的JSTL实现包为例,完整的JSTL包含jstl.jar、standard.jar 和 xalan.jar 三个 jar 包。sun 公司提供的jstl.jar 包封装了 JSTL所要求的一些API 接口和类,Apache Jakarta 小组编写的 JSTL API 实现类封装在standard.jar包中。由于 JDK 在 JDK 1.5 版本中才引入了 XPath API,而 apache Jakarta 小组开发的 JSTL API 是在 JDK1.5 之前推出的,所以 apache Jakarta 小组在 JSTL 中使用的是他们自己开发的 XPath API,这些 API 封装在 xalan.jar 包中。Standard.jar 包中包括核心标签库、国际化/格式化标签库、数据库标签库中的标签和标准的EL自定义函数的实现类,xalan.jar 包中包括 JSTL 解析 XPath 的相关 API 类。

8.2.2  安装和测试JSTL

1.下载JSTL包

可以从Apache的网站下载JSTL的jar包。进入http://jakarta.apache.org/taglibs/ 页面,根据相关的提示信息下载 zip压缩文件,这个压缩文件中包含JSTL的三个jar包。

提示:由于网站的不断升级,下载JSTL 的jar包的网址可能改变。读者在下载这些文件时,如果在指定的网址没有下载的链接,可以进入Apache的网站首页,然后根据相关的提示下载所需的文件。例如,笔者在编写JSTL初稿时,从http://jakarta.apache.org/taglibs/ 下载的压缩文件中只包含jstl.jar和standard.jar两个 jar 包,而 xalan.jar包要从 http://xml.apache.org/xalan-j/downloads.html 下载。

2.安装JSTL

在Tomcat中安装JSTL很简单,只要将 JSTL的jar包复制到<WEB应用程序>\WEB-INF\lib目录下即可。本章中所有的例子程序都在一个名为JSTL的WEB应用程序中运行,所以,将JSTL的三个jar包复制到 <TOMCAT_HOME>\webapps\JSTL\WEB-INF\lib目录下即完成了JSTL的安装

提示:Tomcat 5.5自带的JSP例子应用程序中提供了使用JSTL标签的例子,在<TOMCAT_HOME>\webapps\jsp-examples\WEB-INF\lib目录中有JSTL的jstl.jar包和standard.jar包,所以,对于读者来说,如果不使用JSTL的XML标签库中的标签,可以直接将这两个包复制到<WEB应用程序的主目录>\WEB-INF\lib目录中,而不用事先从Apache的网站下载JSTL的jar包。

3.测试JSTL

安装完JSTL后,接下来就可以准备测试JSTL安装得是否成功了。

Apache小组在提供JSTL时,同时也提供了JSTL标签的标签描述文件(即TLD文件),它们都封装在standard.jar包中,位于该jar文件的META-INF目录中。用WinRAR 打开standard.jar包,进入META-INF目录,可以看到JSTL标签的标签库描述文件。查看这些TLD源文件,可以看到文件中定义的 <uri> 元素的内容与表8.1中列出的标签库的URI相同。因为standard.jar 包中封装了JSTL标签的标签库描述文件,Tomcat在启动时会自动加载standard.jar包中的META-INF目录下的所有TLD文件,所以使用JSTL时不必在web.xml文件中配置JSTL标签库的URI信息,就可以在JSP文件中直接使用JSTL标签。

要在JSP文件中使用JSTL,首先就需要使用taglib指令导入所需要使用的JSTL标签库,taglib指令的uri属性必须为相应标签库的TLD文件中的<uri>元素的值,taglib指令的prefix属性可以自己随意指定,但最好是采用表8.1中的建议前缀。在JSTL应用程序的主目录中按例程8-2编写一个包含JSTL标签的简单JSP文件test.jsp。

例程8-2 test.jsp

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

 

上面的test.jsp页面中的<c:out>标签用于向浏览器输出文本内容,它属于JSTL的核心标签库中的标签由于在test.jsp文件中只使用了JSTL的核心标签库中的标签,所以,只需要使用一条taglib指令导入JSTL的核心标签库,而不必使用多条taglib指令导入JSTL的所有标签库。

启动Tomcat后,在浏览器地址栏中输入test.jsp页面的地址进行访问,如果浏览器中显示出了“hello world!”,说明JSTL安装成功。

8.3  核心标签库

JSTL核心标签库包含了一组用于实现WEB应用中的通用操作的标签,JSP规范为核心标签库建议的前缀名为c。

8.3.1  <c:out>标签

<c:out> 标签用于输出一段文本内容到pageContext对象当前保存的“out”对象中,在通常情况下,pageContext对象当前保存的“out”对象的数据是输出到客户端浏览器,所以,<c:out> 标签通常用于输出一段文本内容到客户端浏览器。如果<c:out> 标签输出的文本内容中包含了需要进行转义的HTML特殊字符,例如,<、>、'、"、&等,<c:out> 标签默认对它们按表8.2进行HTML编码转换后再进行输出,这样就可以在浏览器中显示出这些字符。

表8.2 特殊字符转换

 

<c:out>标签标签有两种语法格式:

语法1,没有标签体的情况:

<c:out value="value"

    [escapeXml="{true|false}"]

   [default="defaultValue"] />

语法2,有标签体的情况,在标签体中指定输出的默认值:

<c:out value="value"

   [escapeXml="{true|false}"] >

default value

</c:out>

<c:out>标签的属性说明如表8.3所示。

8.3  <c:out>标签的属性

属性名

是否支持EL

属性类型

value

true

Object

指定要输出的内容

escapeXml

true

Boolean

指定是否将><&'" 等特殊字符进行HTML编码转换后再进行输出。默认值为true

default

true

Object

指定如果value属性的值为null时所输出的默认值

 

当且仅当value属性的值为null时,<c:out> 标签输出默认值;如果没有指定默认值,默认为空字符串。<c:out> 标签的标签体的内容用于指定输出的默认值,如果value属性的值不为null,即使标签体部分不为空,标签体的内容也不会被输出。如果value属性不是指向一个java.io.Reader对象,<c:out> 标签将调用这个对象的toString方法,然后输出获得的字符串内容。如果value属性指向一个java.io.Reader对象,<c:out> 标签将从这个Reader对象中读取数据后进行输出,当有大量数据要被输出时,将这些数据以Reader对象的形式提供给<c:out>标签进行输出,将会极大提高系统性能。当采用escapeXml属性的默认设置值true时,<c:out>标签将对要输出的特殊字符按表8.2进行转换;如果将escapeXml属性设置为false<c:out>标签将不对要输出的特殊字符进行转换,而是直接输出这些特殊字符。

例程8-3<c:out> 标签的一个演示例子程序。

例程8-3  c_out1.jsp

 

 

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

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

<%

session.setAttribute("test_session", "testValue_session");

%>

直接输出一个字符串:

<c:out value="test" default="123456" /><br /><hr />

输出Web域中不存在的属性的值:<br />

<c:out value="${test_request}" default="这个属性不存在"/><br /><hr />

输出Web域中的test_session属性的值:<br />

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

 

 
  

 

例程8-3的运行结果如图8.1所示。

 

8.1

例程8-4和例程8-5是两个用于演示<c:out>标签的escapeXml属性的例子程序,例程8-4没有设置escapeXml属性,例程8-5escapeXml属性设置为了false

例程8-4  c_out2.jsp

 

 

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

<c:out value="${null}" >

<meta http-equiv="refresh" content="0;url=http://www.it315.org" />

</c:out>

 

 

例程8-5 c_out3.jsp

 

 

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

<c:out value="${null}" escapeXml="false">

<meta http-equiv="refresh" content="0;url=http://www.it315.org" />

</c:out>

 

 
  

 

例程8-4和例程8-5的运行结果分别如图8.2和图8.3所示。

 

8.2

 

8.3

8.3.2  <c:set>标签

<c:set>标签用于设置各种Web域中的属性,或者设置Web域中的java.util.Map类型的属性对象或JavaBean类型的属性对象的属性。<c:set>标签有四种语法格式:

语法1,使用value属性设置指定域中的某个属性的值:

<c:set value="value"

    var="varName"

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

语法2,在标签体中设置指定域中的某个属性的值:

<c:set var="varName"

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

body content

</c:set>

语法3,使用value属性设置Web域中的一个属性对象的某个属性:

<c:set value="value"

    target="target"

    property="propertyName" />

语法4,在标签体中设置Web域中的一个属性对象的某个属性性:

<c:set target="target"

    property="propertyName">

body content

</c:set>

<c:set>标签的属性说明如表8.4所示。

8.4 <c:set>标签的属性

属性名

是否支持EL

属性类型

value

true

Object

用于指定属性值

var

false

String

用于指定要设置的Web域属性的名称

scope

false

String

用于指定属性所在的Web

target

true

Object

用于指定要设置属性的对象,这个对象必须是JavaBean对象或java.util.Map对象

property

true

string

用于指定当前要为对象设置的属性名称

 

如果使用第1种语法格式时的value属性值为null,或者使用第2种语法格式时的标签体内容为空,<c:set>标签将从scope属性指定的域范围中删除var属性指定的属性。

在第3种语法格式和第4语法格式中,如果target属性的值是java.util.Map对象,property属性表示该Map对象的关键字,如果Map对象没有指定的关键字,就给Map对象增加指定的关键字;如果target属性的值是JavaBean对象,property属性表示JavaBean对象的属性,如果value的类型与JavaBean属性的类型不匹配时,会根据EL的转换规则自动进行转换。当使用第3种语法格式或第4种语法格式时,如果target属性的值为null(即target属性指定的对象不存在),或者target属性的值是一个JavaBean对象,但该JavaBean中不存在property属性指定的属性,<c:set>标签将抛出异常。如果使用第3种语法格式时value属性的值为null,或者使用第4种语法格式时标签体的内容为空,如果target属性的值是一个java.util.Map对象,就从Map对象中删除property属性指定的关键字对应的项;如果target属性的值是一个JavaBean对象,就将JavaBean的相应属性的值设置为null

例程8-6是使用<c:set>标签设置某个Web域中的属性的一个演示例子程序。

例程8-6 c_set1.jsp

 

 

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

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

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

zxx

</c:set>

输出session 作用域中的userNamer属性的值:

<c:out value="${userName}" /><br /><hr />

输出session 作用域中的bookname属性的值:

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

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

 

 

例程8-6运行结果如图8.4所示

 

8.4

例程8-7是使用<c:set>标签设置UserBean对象和Map对象的属性的一个演示例子程序。

例程8-7  c_set2.jsp

 

 
  

 

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

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

<%@ page import="java.util.HashMap" %>

<jsp:useBean id="user" class="org.it315.UserBean" />

<% 

HashMap preferences = new HashMap();

session.setAttribute("preferences",preferences);

%>

设置和输出UserBean对象的userName属性值:

<c:set value="zxx" target="${user}" property="userName" />

<c:out value="${user.userName}" /><br /><hr />

设置和输出UserBean对象的password属性值:

<c:set target="${user}" property="password" />

<c:out value="${user.password}" /><br /><hr />

设置和输出Map对象的color关键字的值:

<c:set target="${preferences}" property="color" value="${param.color}" />

<c:out value="${preferences.color}" />

 

 
  

 

在浏览器地址栏输入如下地址访问例程8-7

http://localhost:8080/JSTL/c_set2.jsp?color=green

例程8-7的运行结果如图8.5所示。

 

8.5

8.3.3  <c:remove>标签

<c:remove>标签用于删除各种Web域中的属性,其语法格式如下:

<c:remove var="varName"

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

var属性用于指定要删除的属性的名称,scope属性用于指定要删除的属性所属的Web域,它们的值都不能接受动态值。如果没有指定scope属性,<c:remove>标签就调用PageContext.removeAttribute(varName)方法,否则就调用PageContext.removeAttribute(varName, scope) 方法。<c:remove><c:set>标签第一种语法格式的value属性值为null时的作用相同。

例程8-8是使用<c:remove>标签的一个演示例子程序。

例程8-8 c_remove.jsp

 

 

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

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

<c:set value="org.it315" var="company" scope="request" />

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

a123456a

</c:set>

在没有使用 &lt;c:remove&gt; 标签之前,变量的值为:<br />

company:<c:out value="${company}" /><br />

passWord:<c:out value="${passWord}" /><br />

<c:remove var="company" scope="request" />

<!--c:set var="company" scope="request" 此行与黑体字的一行作用是一样的/-->

<c:remove var="passWord" scope="session" /><hr />

在使用 &lt;c:remove&gt; 标签之后,变量的值为:<br />

company:<c:out value="${company}" /><br />

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

 

 

 
  

 

例程8-8运行结果如图8.6示。

 

8.6

8.3.4  <c:catch>标签

<c:catch>标签用于捕获嵌套在标签体中的内容抛出的异常,其语法格式如下:

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

nested actions

</c:catch>

var属性用于标识<c:catch>标签捕获的异常对象,其值是一个静态的字符串,不支持动态属性值。<c:catch>标签将捕获的异常对象以var指定的名称保存到page这个Web域中,如果没有指定var属性,则<c:catch>标签仅捕获异常,不在page域保存异常对象。如果<c:catch>标签体中的内容没有抛出异常,<c:catch>标签将从page域中删除var属性指定的属性。

<c:catch>标签可以捕获任何标签抛出的异常,并且可以同时处理多个标签抛出的异常,这样,可以对JSP页面的异常进行统一处理,显示给用户一个更友好的页面。JSP处理异常的通用机制是出现重要异常后跳转到错误处理页面,建议尽量不要用<c:catch>标签来代替JSP的错误处理机制,只对一些次要异常才使用<c:catch>标签进行捕获处理。

例程8-9是使用<c:catch>标签进行异常捕获处理的一个演示例子程序。

8-9  c_catch.jsp

 

 
  

 

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

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

<c:catch var="myex">

<%

int i = 0;

int j = 10;

out.println(j + "/" + i + "=" + j/i);

%>

</c:catch>

异常:<c:out value="${myex}" /><br />

异常 myex.getMessage<c:out value="${myex.message}" /><br />

异常 myex.getCause<c:out value="${myex.cause}" /><br />

异常 myex.getStackTrace<c:out value="${myex.stackTrace}" />

 

 
  

 

在例程8-9中<c:catch>标签内嵌套的脚本元素抛出了异常,<c:catch var="myex">将捕获到这个异常,调用<c:out value="${myex.message}" />,相当于调用<%=myex.getMessage()%>例程8-9运行结果如图8.7所示

 

8.7

8.3.5  <c:if>标签

JSP页面的显示逻辑中也经常需要进行条件判断,<c:if>标签可以构造简单的“if-then”结构的条件表达式,如果条件表达式的结果为真就执行标签体部分的内容。<c:if>标签有两种语法格式:

语法1,没有标签体的情况:

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

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

语法2,有标签体的情况,在标签体中指定要执行的内容:

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

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

    body content

</c:if>

<c:if>标签的属性说明如表8.5所示。

表8.5   <c:if>标签的属性

属性名

是否支持EL

属性类型

test

true

boolean

决定是否处理标签体中的内容的条件表达式

var

false

String

用于指定将test属性的执行结果保存到某个Web域中的某个属性的名称

scope

false

String

指定将test属性的执行结果保存到哪个Web域中

 

对于语法2,如果指定了<c:if>标签的scope属性,则必须指定var属性。

例程8-10是使用<c:if>标签的一个演示例子程序。

例程8-10  c_if.jsp

 

 
  

 

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

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

<jsp:useBean id="user" class="org.it315.UserBean" />

<c:set value="${param.count}" target="${user}" property="visitCount" />

<c:if test="${user.visitCount == 1}">

这是您第一次访问本网站,欢迎您!

</c:if>

 

 
  

 

在浏览器地址栏输入如下地址访问例程8-10

http://localhost:8080/JSTL/c_if.jsp?count=1

例程8-10运行结果如8.8示。

 

8.8

8.3.6  <c:choose>标签

<c:choose>标签用于指定多个条件选择的组合边界,它必须与<c:when>和<c:otherwise>标签一起使用。使用<c:choose>,<c:when>和<c:otherwise>三个标签,可以构造类似 “if-else if-else” 的复杂条件判断结构。

<c:choose>标签没有属性,在它的标签体内只能嵌套一个或多个<c:when>标签和0个或一个<c:otherwise>标签,并且同一个<c:choose>标签中的所有<c:when>子标签必须出现在<c:otherwise>子标签之前。如果<c:choose>标签内嵌套一个<c:when>标签和<c:otherwise>标签,就相当于“if-else”的条件判断结构;如果<c:choose>标签内嵌套多个<c:when>标签和一个<c:otherwise>标签,就相当于“if-else if-else”标签。

<c:when>标签只有一个test属性,该属性的值为布尔类型。test属性支持动态值,其值可以是一个条件表达式,如果条件表达式的值为true,就执行这个<c:when>标签体的内容。<c:when>标签体的内容可以是任意的JSP代码。<c:otherwise>标签没有属性,它必须作为<c:choose>标签的最后分支出现。

当JSP页面中使用<c:choose>标签时,嵌套在<c:choose>标签内的test条件成立的第一个<c:when>标签的标签体内容将被执行和输出。当且仅当所有的<c:when>标签的test条件都不成立时,才执行和输出<c:otherwise>标签的标签体内容。如果所有的<c:when>标签的test条件都不成立,并且<c:choose>标签内没有嵌套<c:otherwise>标签,则不执行任何操作。

例程8-11是使用<c:choose>、<c:when>、<c:otherwise>标签的一个演示例子程序。

8-11 c_choose.jsp

 

 

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

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

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

<c:choose>

<c:when test="${count == 0}">

    对不起,没有符合您要求的记录。

</c:when>

<c:otherwise>

    符合您要求的记录共有${count}.

</c:otherwise>

</c:choose>

 

 

在浏览器地址栏输入如下地址访问例程8-11

http://localhost:8080/JSTL/c_choose.jsp?count=0

例程8-11的运行结果如图8.9所示。如果将参数count的值修改为10,则运行结果如图8.10所示。

 

 

 

8.9

 

8.10

 

例程8-12是一个综合使用<c:if>标签和<c:choose>等标签的例子程序,在这个例子程序中,首先使用<c:if>标签判断表单提交的方式是否是POST,如果是,就再使用<c:choose>等标签根据表单提交的内容进行不同的处理。

程8-12  c_customLogic.jsp

 

 

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

<c:if test="${pageContext.request.method=='POST'}">

       <c:choose>

              <c:when test="${param.favor == 'computer'}">

                     Your favourite course is <b>computer</b>.

              </c:when>

              <c:otherwise>

                     Your favourite course is <i>other</i>.

              </c:otherwise>

       </c:choose>

</c:if>

<form method="POST">What is your favourite course?<br/>

<input type="text" name="favor" />

<input type="submit" value="submit" />

</form>

 

 

在浏览器地址栏中直接访问c_customLogic.jsp运行结果如8.11示,在文本框中输入“computer”,单击其中的submit按钮后的运行结果如图8.12示。

 

8.11

 

8.12

8.3.7  <c:forEach>标签

JSP页面的显示逻辑中也经常需要对集合对象进行循环迭代操作,<c:forEach>标签用于对一个集合对象中的元素进行循环迭代操作,或者按指定的次数重复迭代执行标签体中的内容。<c:forEach>标签有两种语法格式:

语法1,在集合对象中迭代:

<c:forEach [var="varName"]

            items="collection"

            [varStatus="varStatusName"]

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

    body content

</c:forEach>

语法2,迭代固定的次数:

<c:forEach [var="varName"]

            [varStatus="varStatusName"]

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

    body content

</c:forEach>

<c:forEach>标签的属性说明如表8.6所示。

表8.6  <c:forEach>标签的属性

属性名

是否支持EL

属性类型

属 性 描 述

var

false

String

指定将当前迭代到的元素保存到page这个Web域中的属性名称

items

true

任何支持的类型

将要迭代的集合对象

varStatus

false

String

指定将代表当前迭代状态信息的对象保存到page这个Web域中的属性名称

begin

true

int

如果指定items属性,就从集合中的第begin个元素开始进行迭代,begin的索引值从0开始编号;如果没有指定items属性,就从begin指定的值开始迭代,直到end值时结束迭代

end

true

int

参看begin属性的描述

step

true

int

指定迭代的步长,即迭代因子的迭代增量

 

在使用<c:forEach>标签时,需要注意如下几点说明:

  1. 如果指定begin属性,其值必须大于或等于零;
  2. 如果指定步长(step属性),其值必须大于或等于1;
  3. 如果items属性的值为null,则要处理的集合对象为空,这时不执行迭代操作;
  4. 如果指定的begin属性的值大于或等于集合对象的长度,不执行迭代操作;
  5. 如果指定的end属性的值小于begin属性的值,不执行迭代操作;

 

<c:forEach>标签的items属性的值支持下面的数据类型:

  1. 任意类型的数组
  2. java.util.Collection
  3. java.util.Iterator
  4. java.util.Enumeration
  5. java.util.Map
  6. String

items属性还支持与数据库有关的数据类型java.sql.ResultSet(包括javax.sql.RowSet),这些数据类型将在8.5 节的数据库标签中进行介绍。对字符串的迭代操作通常使用<c:forTokens>标签或JSTL函数,例如fn:split和fn:jion,JSTL函数将在8.7节进行介绍。

 

1.迭代Collection类型的集合对象

例程8-13是使用<c:forEach>标签迭代Collection类型的集合对象的一个应用举例。

例程8-13  c_forEach_collection.jsp

 

 
  

 

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

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

<%@ page import="java.util.*,org.it315.UserBean" %>

<%

       Collection users = new ArrayList();

       for(int i=0; i<5; i++)

       {

              UserBean user = new UserBean();

              user.setUserName("user" + i);

              user.setPassword("guess" + i);

              users.add(user);

       }

       session.setAttribute("users", users);

%>

       <div style="text-align:center">User List

       <table border="1">

              <tr><td>用户名</td><td>密码</td></tr>

              <c:forEach var="user" items="${users}">

                     <tr>

                            <td>${user.userName}</td><td>${user.password}</td>

                     </tr>

              </c:forEach>

       </table></div>

 

 
  

 

例程8-13的运行结果如图8.13所示。

 

图8.13

2.迭代Map对象

使用<c:forEach>标签迭代Map类型的集合对象时,迭代出的每个元素的类型为Map.Entry,Map.Entry代表Map集合中的一个条目项,其中的getKey()方法可获得条目项的关键字,getValue()方法可获得条目项的值。

EL中的requestScope隐含对象代表request作用域中的所有属性的Map对象,所以我们可以使用<c:forEach>标签迭代输出EL中的requestScope隐含对象中的所有元素,如例程8-14所示。

例程8-14 c_forEach_map.jsp

 

 
  

 

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

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

       <%

              request.setAttribute("attr1","value1");

              request.setAttribute("attr2","value2");

       %>

       <div style="text-align:center">Properties(Map)

       <table border="1">

              <tr><td>Map的关键字</td><td>Map的对应关键字的值</td></tr>

              <c:forEach var="entry" items="${requestScope}">

                     <tr><td>${entry.key}</td><td>${entry.value}</td></tr>

              </c:forEach>

       </table></div>

 

 

例程8-14的运行结果如图8.14所示。

 

图8.14

3.迭代指定的次数

<c:forEach>标签可以按指定的次数重复迭代执行标签体中的内容,使用这种方式迭代时,可以指定迭代的步长。例程8-15中分别演示了指定迭代步长和没有指定迭代步长的情况。

例程8-15 c_forEach_count.jsp

 

 
  

 

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

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

1116迭代,指定迭代步长为2

<c:forEach var="i" begin="11" end="16" step="2">

       ${i}

</c:forEach><br /><hr />

05迭代,没有指定迭代步长:

<c:forEach var="i" begin="0" end="5">

       ${i}

</c:forEach>

 

 

例程8-15的运行结果如图8.15所示。

 

图8.15

4.指定迭代集合对象的范围和步长

<c:forEach>标签迭代集合类对象时,也可以指定迭代的范围和步长,如例程8-16所示。

例程8-16 c_forEach_col.jsp

 

 
  

 

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

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

<%@ page import="java.util.*,org.it315.UserBean" %>

<%

    Collection users = new ArrayList();

    for(int i=0; i<6; i++)

    {

        UserBean user = new UserBean();

        user.setUserName("user" + i);

        user.setPassword("guest" + i);

        users.add(user);

    }

    session.setAttribute("users", users);

%>

<div style="text-align:center">User List(指定迭代范围和步长)

<table border="1">

    <tr><td>用户名</td><td>密码</td></tr>

    <c:forEach var="user" items="${users}" end="5" step="2">

        <tr>

            <td>${user.userName}</td><td>${user.password}</td>

        </tr>

    </c:forEach>

</table></div>

 

 
  

 

例程8-16的运行结果如图8.16所示。

 

图8.16

5.获取迭代的状态信息

不管是迭代集合对象,还是迭代指定的次数,在迭代时都可以获得当前的迭代状态信息。<c:forEach>标签可以将代表当前迭代状态信息的对象保存到page中,varStatus属性指定了这个对象保存在page域中的属性名称。代表当前迭代状态信息的对象的类型为javax.servlet.jsp.jstl.core.LoopTagStatus,从JSTL规范中可以查看到这个类的详细信息,其中定义了如下一些方法:

  1. public java.lang.Integer getBegin()
    返回为标签设置的begin属性的值,如果没有设置begin属性则返回null
  2. public int getCount()
    返回当前已循环迭代的次数
  3. public java.lang.Object getCurrent()
    返回当前迭代到的元素对象
  4. public java.lang.Integer getEnd()
    返回为标签设置的end属性的值,如果没有设置end属性则返回null
  5. public int getIndex()
    返回当前迭代的索引号
  6. public java.lang.Integer getStep()
    返回为标签设置的step属性的值,如果没有设置step属性则返回null
  7. public boolean isFirst()
    返回当前是否是第一次迭代操作
  8. public boolean isLast()
    返回当前是否是最后一次迭代操作

 

例程8-17是一个获取迭代状态信息的例子程序。

例程8-17 c_forEach_col2.jsp

 

 
  

 

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

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

<%@ page import="java.util.*,org.it315.UserBean" %>

<%

    Collection users = new ArrayList();

    for(int i=0; i<6; i++)

    {

        UserBean user = new UserBean();

        user.setUserName("user" + i);

        user.setPassword("guest" + i);

        users.add(user);

    }

    session.setAttribute("users", users);

%>

<div style="text-align:center">User List

<table border="1">

    <tr><td>用户名</td><td>密码</td><td>index</td>

    <td>count</td><td>first?</td><td>last?</td></tr>

    <c:forEach var="user" items="${users}" varStatus="sta" begin="1" step="2">

        <tr>

            <td>${user.userName}</td><td>${user.password}</td>

            <td>${sta.index}</td><td>${sta.count}</td>

            <td>${sta.first}</td><td>${sta.last}</td>

        </tr>

    </c:forEach>

</table></div><hr>

 

<div style="text-align:center">迭代固定的次数

<table border="1">

    <tr><td>数值</td><td>index</td><td>count</td>

    <td>first?</td><td>last?</td></tr>

    <c:forEach var="i" varStatus="sta1" begin="101" end="103">

        <tr>

            <td>${i}</td><td>${sta1.index}</td><td>${sta1.count}</td>

            <td>${sta1.first}</td><td>${sta1.last}</td>

        </tr>

    </c:forEach>

</table></div>

 

 
  

 

例程8-17的运行结果如图8-17所示。

 

图8.17

6.与条件标签结合使用

迭代标签可以与条件标签结合使用,对数据进行有条件的迭代,应用举例如例程8-18所示。

例程8-18 c_forEach_com.jsp

 

 
  

 

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

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

<div style="text-align:center">

<table border="1"><tr><td>number</td><td>count</td><td>even|odd</td></tr>

<c:forEach var="i" begin="11" end="13" varStatus="status">

       <tr><td>${i}</td><td>${status.count}</td><td>

       <c:choose>

              <c:when test="${status.count % 2 == 0}">

                     偶数行

              </c:when>

              <c:otherwise>

                     奇数行

              </c:otherwise>

       </c:choose></td></tr>

</c:forEach>

<table></div>

 

 

例程8-18的运行结果如图8.18所示。

 

图8.18

8.3.8  <c:forTokens>标签

<c:forTokens>标签专门用于实现类似java.util.StringTokenizer类的迭代功能,但它是以单个字符作为分隔符,同时可以指定多个字符作为多个并行的分隔符。<c:forTokens>标签的语法格式如下:

<c:forTokens items="stringOfTokens" delims="delimiters"

      [var="varName"]

      [varStatus="varStatusName"]

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

body content

</c:forTokens>

<c:forTokens>标签的属性说明如表8.7所示。

8.7  c:forTokens

属性名

是否支持EL

属性类型

属 性 描 述

var

false

String

指定将当前迭代出的子字符串保存到page这个Web域中的属性名称

items

true

String

将要迭代的字符串

delims

true

String

指定一个或多个分隔符

varStatus

false

String

指定将代表当前迭代状态信息的对象保存到page这个Web域中的属性名称,代表当前迭代的状态信息的对象的类型为javax.servlet.jsp.jstl.core.LoopTagStatus,从JSTL规范中可以查看这个类的详细信息

begin

true

int

指定从第begin个子字符串开始进行迭代,begin的索引值从0开始编号

end

true

int

指定迭代到第begin个子字符串,begin的索引值从0开始编号

step

true

int

指定迭代的步长,即每次迭代后的迭代因子增量

 

在使用<c:forTokens>标签时,需要注意如下几点说明

  1. 如果指定begin属性,其值必须大于或等于零。
  2. 如果指定步长(step属性),其值必须大于或等于1。
  3. 如果指定的end属性的值小于begin属性的值,不执行迭代操作。

例程8-19是一个使用<c:forTokens>标签的例子程序。

例程8-19 c_forTokens.jsp

 

 

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

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

使用"|"作为分隔符<br />

<c:forTokens var="token" items="spring,summer|autumn,winter" delims="|">

${token}&copy;

</c:forTokens><br />

使用"|"","作为分隔符<br />

<c:forTokens var="token" items="spring,summer|autumn,winter" delims="|," end="3">

${token}&copy;

</c:forTokens><br />

使用"-"作为分隔符<br />

<c:forTokens var="token" items="year--season--month-week" delims="-">

${token}&copy;

</c:forTokens>

 

 

例程8-19运行结果如图8.19 所示。

 

8.19

8.3.9  URL相关的标签概述

JSTL核心标签库中提供了如下一些与URL操作相关的标签:

  1. <c:import>
  2. <c:url>
  3. <c:redirect>
  4. <c:param>

在举例讲解上面的某些标签时,要引用另外一个JSP文件,这里先创建好这个JSP文件,如例程8-20所示。

例程8-20 register.jsp

 

 

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

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

<%

    String name = request.getParameter("name");

    name = new String(name.getBytes("iso-8859-1"),"gb2312");

    session.setAttribute("name",name);

    String country = request.getParameter("country");

    country = new String(country.getBytes("iso-8859-1"),"gb2312");

    session.setAttribute("country",country);

%>

name=${name};

country=${country}<br />

 

8.3.10  <c:param>标签

在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。<c:param>标签可以嵌套在<c:import>、<c:url>或<c:redirect>标签内,为这些标签所使用的URL地址附加参数。<c:param>标签在为一个URL地址附加参数时,将自动对参数值进行URL编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到URL地址后面,这也就是使用<c:param>标签的最大好处。<c:param>标签有两种语法格式:

语法1,使用value属性指定参数的值:

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

语法2,在标签体中指定参数的值:

<c:param name="name">

    parameter value

</c:param>

<c:param>标签的属性说明如表8.8所示。

表8.8  <c:param>标签的属性

 

8.3.11  <c:url>标签

<c:url>标签用于在JSP页面中构造一个URL地址,其主要目的是实现URL重写。URL重写就是将会话标识号以参数形式附加在URL地址后面,详细细节请参看笔者编著的《深入体验java Web开发内幕——核心基础》一书中的第7.4.8的讲解。<c:url>标签有两种语法格式:

语法1,没有标签体的情况:

<c:url value="value"

        [context="context"]

        [var="varName"]

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

语法2,有标签体的情况,在标签体中指定构造的URL的参数:

<c:url value="value"

        [context="context"]

        [var="varName"]

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

    <c:param>标签

</c:url>

<c:url>标签的属性说明如表8.9所示。

表8.9  <c:url>标签的属性

属性名

是否支持EL

属性类型

value

true

String

指定要构造的URL

context

true

String

当要使用相对路径导入同一个服务器下的其他WEB应用程序中的URL地址时,context属性指定其他WEB应用程序的名称

var

false

String

指定将构造出的URL结果保存到Web域中的属性名称

scope

false

String

指定将构造出的URL结果保存到哪个Web域中

 

value属性所指定的URL可以是相对路径和绝对路径,其具体细节与8.3.12节的<c:import>标签的url属性的细节相同。使用<c:url>标签构造URL时,可以通过嵌套的<c:param>标签指定参数,或在value属性中直接指定参数。

例程8-21是一个使用<c:url>标签的例子程序。

例程8-21 c_url.jsp

 

 

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

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

使用绝对路径构造URL:

<c:url value="http://localhost:8080/JSTL/URL/register.jsp" var="myUrl1">

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

       <c:param name="country" value="${param.country}" />

</c:url>

<a href="${myUrl1}">Register1</a><hr />

使用相对当前JSP页面的路径构造URL:

<c:url value="register.jsp?name=wangwu&country=France" var="myUrl2" />

<a href="${myUrl2}">Register2</a><hr />

使用相对当前WEB应用的路径构造URL:

<c:url value="/URL/register.jsp?name=zhaoliu&country=England" var="myUrl3" />

<a href="${myUrl3}">Register3</a>

 

 

在浏览器地址栏输入如下地址访问例程8-21

http://localhost:8080/JSTL/c_url.jsp?country=China

查看例程8-21运行结果的源文件,内容如例程8-22所示。

例程8-22 c_url.jsp运行结果的源文件

 

 

使用绝对路径构造URL:

<a href="http://localhost:8080/JSTL/URL/register.jsp?name=%d5%c5%c8%fd&country=China">Register1</a><hr />

使用相对当前JSP页面的路径构造URL:

<a href="register.jsp?name=wangwu&country=France">Register2</a><hr />

使用相对当前WEB应用的路径构造URL:

<a href="/JSTL/URL/register.jsp?name=zhaoliu&country=England">Register3</a>

 

 

8.3.12  <c:import>标签

<c:import>标签用于在JSP页面中导入一个URL地址指向的资源内容,其作用有点类似<jsp:include>这个JSP标准标签,但功能更强大<c:import>标签标签有两种语法格式:

语法1,将URL地址指向的资源内容以字符串形式输出或以字符串形式保存到一个变量中:

<c:import url="url"

              [context="context"]

              [var="varName"]

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

              [charEncoding="charEncoding"]>

       optional body content for <c:param> subtags

</c:import>

语法2,将URL地址指向的资源内容保存到一个Reader对象中:

<c:import url="url"

              [context="context"]

              varReader="varReaderName"

              [charEncoding="charEncoding"]>

       body content where varReader is consumed by another action

</c:import>

<c:import>标签的属性说明如表8.10所示。

8.10  <c:import>标签的属性

属性名

是否支持EL

属性类型

url

true

String

指定要导入的资源的URL地址

context

true

String

当要使用相对路径导入同一个服务器下的其他WEB应用程序中的资源时,context属性指定其他WEB应用程序的名称

var

false

String

指定将导入的资源内容保存到Web域中的属性名称

scope

false

String

指定将导入的资源内容保存到哪个Web域中

charEncoding

true

String

将导入的资源内容转换成字符串时所使用的字符集编码

varReader

false

String

指定将导入的资源内容保存到page域中的一个java.io.Reader对象中,varReader属性指定了该Reader对象在page这个Web域中的属性名称

 

使用<c:import>标签导入其他资源文件时,如果被导入的资源文件中含有非ASCII码字符,必须注意指定charEncoding属性,否则可以不设置这个属性。

当使用第1种语法格式时,如果指定了var属性,导入的资源内容以字符串形式保存到一个变量中,var属性指定了这个变量保存在Scope属性指定的Web域中的名称;如果没有指定var属性,导入的资源内容将以字符串形式直接输出。第1种语法格式的标签体内中可以嵌套<c:param>标签来给导入的资源传递参数。

使用第2种语法格式时,导入的资源内容保存到page域中的一个java.io.Reader对象中,varReader属性指定了该Reader对象在page这个Web域中的属性名称。因为<c:import>标签在标签结束时将关闭Reader对象对应的输入流,所以varReader属性指定的变量只在标签内有效。第2种语法格式的标签体内中应该且只能嵌套调用varReader属性指定的reader对象的其他标签。当使用第2种语法格式时,<c:import>标签体内不能嵌套<c:param>标签,如果要给导入的资源传递参数,则必须在url属性值中设置好这些参数,这时可以使用<c:url>标签来创建一个带参数的URL地址,此时如果有必要,<c:import>标签将删除导入资源中的所有URL重写后产生的session id信息。

 

使用<c:import>标签导入外部资源文件时,url属性可以设置为被导入资源的相对URL或绝对URL

1.使用相对URL导入其他资源文件

(1)当被导入的外部资源文件与当前JSP文件属于同一个WEB应用程序时,JSP容器对资源路径的处理与<jsp:include>标签相同,这时路径可以以“/”字符开始,也可以以文件名或目录名开始。如果路径以“/”字符开始,则被导入资源文件的路径是相对于JSP页面所在的WEB应用的根路径;如果路径以文件名或目录名开始,则被导入的资源文件的路径就是相对于当前的JSP页面的路径。

(2)当被导入的外部资源文件与JSP文件属于同一个服务器下的不同的WEB应用时,如果要使用相对路径,路径必须以“/”开始。此时,<c:import>标签的context属性必须被设置为被导入的资源文件所在的WEB应用程序的名称。注意:使用相对路径导入其它WEB应用程序中的资源时,必须在<TOMCAT_HOME>\conf\server.xml配置文件中将使用<c:import>标签的WEB应用程序的<Context>元素的crossContext属性设置为true,如下所示:

<Context path="/JSTL" docBase="JSTL" crossContext="true"/>

2.使用绝对URL导入其他资源

使用绝对路径导入外部资源文件时,即使被导入的文件与当前JSP文件属于同一个WEB应用程序,被导入的资源文件也不会共享当前JSP页面的request和session对象。因为在这种情况下,当前JSP文件所属的Web服务器充当了一个Http客户端程序,去访问绝对路径所指定的外部资源文件,然后将响应结果的实体内容加入到当前JSP页面中,此时,访问当前JSP页面的客户端是浏览器,而访问绝对路径所指定的外部资源文件的客户端是当前JSP文件所属的Web服务器,这显然属于两个不同的客户端,所以,被导入的资源文件和当前JSP页面不可能共享request和session对象。

程8-23是使用<c:import>标签的一个应用举例。

例程8-23 c_import.jsp

 

 

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

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

使用相对路径导入同一个WEB应用中的资源:<br />

<c:import url="/register.jsp?name=zhangsan">

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

</c:import><hr />

使用相对路径导入同一个服务器下的不同WEB应用中的资源:<br />

<c:import url="/hello.jsp" context="/EL" /><hr />

使用绝对路径导入资源示例1:

<c:import url="http://localhost:8080/EL/hello.jsp" /><hr />

使用绝对路径导入资源示例2:

<c:import url="http://localhost:8080/JSTL/register.jsp" var="myImport"

            charEncoding="gb2312">

    <c:param name="name" value="${param.name}" />

    <c:param name="country" value="中国" />

</c:import>

${myImport}

 

 

在浏览器地址栏输入如下地址访问例程8-23,其运行结果如图8.20 所示:

    http://localhost:8080/JSTL/c_import.jsp?name=zxx&country=China

 

8.20

在<c:import>标签中使用相对路径导入其他资源时,其工作原理与<jsp:include>标签相同,所以,在被导入的资源文件中可以获得传递给当前JSP页面的请求参数,例如,例程8-23中的第一个<c:import>标签并没有在目标url后增加country参数,但是,在register.jsp页面中获得了country参数

8.3.13  <c:redirect>标签

<c:redirect>标签用于将当前的访问请求转发或重定向到其他资源,它可以根据url属性所指定的地址,执行类似<jsp:forward>这个JSP标准标签的功能,将访问请求转发到其他资源;或执行response.sendRedirect()方法的功能,将访问请求重定向到其他资源。<c:redirect>标签有两种语法格式:

语法1,没有标签体的情况:

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

语法2,有标签体的情况,在标签体中指定重定向时的参数:

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

    <c:param>subtags

</c:redirect>

<c:redirect>标签的属性说明如表8.11所示。

表8.11  <c:redirect>标签的属性

属性名

是否支持EL

属性类型

url

true

String

指定要转发或重定向到的目标资源的URL地址

context

true

String

当要使用相对路径重定向到同一个服务器下的其他WEB应用程序中的资源时,context属性指定其他WEB应用程序的名称

 

url属性指定将要重定向的资源的URL时,可以使用相对路径和绝对路径,其具体细节与<c:import>标签的url属性相同。例程8-24是一个使用<c:redirect>标签的应用例子。

例程8-24 c_redirect.jsp

 

 

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

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

<c:url value="http://localhost:8080/JSTL/URL/register.jsp" var="myUrl">

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

    <c:param name="country" value="中国" />

</c:url>

<c:redirect url="${myUrl}" />

 

 

8-24的运行结果如图8.21 所示。

 

8.21

8.4  国际化标签

8.4.1  国际化标签库概述

为了简化WEB应用的国际化开发,JSTL中提供了一个用于实现国际化和格式化功能的标签库,我们将其简称为国际化标签库,JSP规范为国际化标签库建议的前缀名为fmt。国际化标签库中包括了一组用于实现WEB国际化功能的标签,这组标签封装了Java语言中java.util和java.text这两个包中与国际化相关的API类的功能。国际化标签库中提供了绑定资源包和从资源包中的本地资源文件内读取文本内容的标签,也提供了对数值和日期等本地敏感的数据按本地化信息进行显示和解析的标签,还提供了按本地特定的时区来调整时间的标签。

国际化标签库中的标签可以根据客户端浏览器的信息来自动确定本地信息,也可以由JSP页面作者显式地指定本地信息。如果没有特别指定,本章案例假设默认的本地信息为“中文(中国)”。

在举例讲解国际化标签库中的标签时, 通常要使用到包含有多个资源文件的资源包,这个资源包中的各个资源文件分别对应几个不同本地信息。这里先创建好一组资源包文件。首先按例程8-25和例程8-26创建两个名称分别为greetings.properties和temp.properties的资源文件。

例程8-25 greetings.properties

 

 

org.it315.heading=The first WEB application

org.it315.welcome=Welcome to www.it315.org !

org.it315.okKey=Today is {0,date,full}, you have {1,number,currency} dollars.

 

 

例程8-26 temp.properties

 

 

org.it315.heading=第一个WEB应用程序

org.it315.welcome=欢迎访问www.it315.org网站!

org.it315.okKey=今天是{0,date,full},你有{1,number,currency}元。

 

 

接着使用JDK中提供的native2ascii程序将temp.properties文件中的中文字符转换成其Unicode码形式的转义序列。在命令行窗口中进入temp.properties文件所在的目录,执行如下命令:

       native2ascii -encoding GB2312 temp.properties greetings_zh.properties

上面的命令执行后产生的greetings_zh.properties文件内容如例程8-27所示。

例程8-27 greetings_zh.properties

 

 

org.it315.heading=\u7b2c\u4e00\u4e2aWEB\u5e94\u7528\u7a0b\u5e8f

org.it315.welcome=\u6b22\u8fce\u8bbf\u95eewww.it315.org\u7f51\u7ad9!

org.it315.okKey=\u4eca\u5929\u662f{0,date,full}\uff0c\u4f60\u6709{1,number,

currency}\u5143\u3002

 

 

英文本地环境所对应的资源文件只需复制greetings.properties文件并改名为greetings_en.properties即可。最后将greetings.propertiesgreetings_en.propertiesgreetings_zh.properties这三个资源文件保存到<TOMCAT_HOME>\webapps\JSTL\WEB-INF\classes\org\it315\目录中。

8.4.2  <fmt:setLocale>标签

<fmt:setLocale>标签用于在JSP页面中显式地设置用户的本地化信息,并将设置的本地化信息以Locale对象的形式保存在某个Web域中,其在Web域中的属性名称为“javax.servlet.jsp.jstl.fmt.locale”。使用<fmt:setLocale>标签设置本地化信息后,国际化标签库中的其他标签将使用该本地化信息,而忽略客户端浏览器传递过来的本地信息。<fmt:setLocale>标签的语法格式如下:

<fmt:setLocale value="locale"

                     [variant="variant"]

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

<fmt:setLocale>标签的属性说明如表8.12所示。

8.12  <fmt:setLocale>标签的属性

属性名

是否支持EL

属性类型

value

true

String或java.util.Locale

指定用户的本地化信息,可以是一个字符串或java.util.Locale实例对象。如果是字符串,则必须包含小写形式的语言编码,其后也可以带有大写形式的国家编码,两者中间用“-”或“_”连接

variant

true

String

指定创建Locale实例对象时设置的变量部分,它用于标识开发商或特定浏览器为实现扩展功能而自定义的信息

scope

false

String

指定将构造出的Locale实例对象保存在哪个Web作用域中

如果<fmt:setLocale>标签的value属性值为null<fmt:setLocale>标签将采用客户端浏览器传递过来的本地信息。例程8-28是一个使用<fmt:setLocale>标签的例子程序。

例程8-28 fmt_setLocale.jsp

 

 

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

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

<fmt:setLocale value="${param.locale}" />

<fmt:setBundle basename="org.it315.greetings" var="greetings" />

<head>

       <title><fmt:message bundle="${greetings}" key="org.it315.heading" /></title>

</head>

<jsp:useBean id="now" class="java.util.Date" />

<%

       session.setAttribute("number", new Integer(8888888));

%>

<fmt:message bundle="${greetings}" key="org.it315.welcome" /><br />

<fmt:message bundle="${greetings}" key="org.it315.okKey">

       <fmt:param value="${now}" />

       <fmt:param value="${number}" />

</fmt:message>

 

 

例程8-28中的<fmt:setBundle>标签用于根据<fmt:setLocale>标签设置的本地化信息绑定一个资源包,其细节请参看8.4.3节;<fmt:message>标签用于从一个资源包中读取信息并进行格式化输出,其细节请参看8.4.5节;<fmt:param>标签用于为格式化文本串中的占位符设置参数值,其细节请参看8.4.6节。

在浏览器地址栏输入如下地址访问fmt_setLocale.jsp页面

http://localhost:8080/JSTL/fmt_setLocale.jsp?locale=en_US

例程8-28运行结果如8.22

 

8.22

将上面的访问地址中的locale参数设置为zh_CN后访问fmt_setLocale.jsp页面,例程8-28运行结果如图8.23所示。

 

8.23

8.4.3  <fmt:setBundle>标签

<fmt:setBundle>标签用于根据<fmt:setLocale>标签设置的本地化信息创建一个资源包(ResourceBundle)实例对象,并将其绑定到一个Web域的属性上。<fmt:setBundle>标签的语法格式如下:

<fmt:setBundle basename="basename"

            [var="varName"]

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

<fmt:setBundle>标签的属性说明如表8.13所示。

表8.13  <fmt:setBundle>标签的属性

属性名

是否支持EL

属性类型

basename

true

String

指定创建ResourceBundle实例对象的基名

var

false

String

指定将创建出的ResourceBundle实例对象保存到Web域中的属性名称

scope

false

String

指定将创建出的ResourceBundle实例对象保存在哪个Web作用域中

<fmt:setBundle>标签有如下一些特性:

1)如果basename属性的值为null或空字符串,或找不到basename属性指定的资源,<fmt:setBundle>标签保存到Web域中的属性的值为null

2)如果指定了var属性,<fmt:setBundle>标签将把ResourceBundle实例对象以var属性的值作为域属性名称保存到Web域中。

3)如果没有指定var属性,<fmt:setBundle>标签将把ResourceBundle实例对象以域属性名javax.servlet.jsp.jstl.fmt.localizationContext保存到Web域中所有没有嵌套在<fmt:bundle>标签中且未指定bundle属性的<fmt:formatDate>都将使用该标签创建的资源包。

在前面的例程8-28中已经使用了<fmt:setBundle>标签,这里就不再对<fmt:setBundle>标签进行举例讲解了。

8.4.4  <fmt:bundle>标签

<fmt:bundle>标签与<fmt:setBundle>标签的功能类似,但它创建的ResourceBundle实例对象只在其标签体内有效。<fmt:bundle>标签的语法格式如下:

<fmt:bundle basename="basename"

        [prefix="prefix"]>

    body content

</fmt:bundle>

<fmt:bundle>标签的属性说明如表8.14所示。

表8.14  <fmt:bundle>标签的属性

属性名

是否支持EL

属性类型

basename

true

String

指定创建ResourceBundle实例对象的基名

prefix

true

String

指定追加到嵌套在<fmt:bundle>标签内的<fmt:message>标签的key属性值前面的前缀

如果设置了<fmt:bundle>标签的prefix属性,则其中嵌套的<fmt:message>标签的key属性值中就可以省略prefix属性设置的前缀部分,这对要使用具有相同前缀的多个关键字的情况提供了一些简便。

例程8-29是一个使用<fmt:bundle>标签的例子程序,其中分别演示了设置和不设置prefix属性的情况。

例程8-29 fmt_bundle.jsp

 

 

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

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

没有设置prefix属性的情况:<br />

<fmt:bundle basename="org.it315.greetings">

       <fmt:message key="org.it315.heading" /><br />

       <fmt:message key="org.it315.welcome" /><br /><hr />

</fmt:bundle>

设置prefix属性的情况:<br />

<fmt:bundle basename="org.it315.greetings" prefix="org.it315.">

       <fmt:message key="heading" /><br />

       <fmt:message key="welcome" />

</fmt:bundle>

 

 

8-29的运行结果如图8.24示。

 

8.24

8.4.5  <fmt:message>标签

<fmt:message>标签用于从一个资源包中读取信息并进行格式化输出,它有如下一些使用语法格式:

语法1,没有标签体的情况:

<fmt:message key="messageKey"  

            [bundle="resourceBundle"]

            [var="varName"]

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

语法2,在标签体中指定格式化文本串中的占位符参数的情况:

<fmt:message key="messageKey"

            [bundle="resourceBundle"]

            [var="varName"]

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

    <fmt:param>subtags

</fmt:message>

语法3,在标签体中指定消息关键字和可选择的占位符参数:

<fmt:message [bundle="resourceBundle"]

            [var="varName"]

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

    key

    optional <fmt:param>subtags

</fmt:message>

<fmt:message>标签的属性说明如表8.15所示。

表8.15  <fmt:message>标签的属性

属性名

是否支持EL

属性类型

key

true

String

指定要输出的信息的关键字

bundle

true

LocalizationContext

指定ResourceBundle对象在Web域中的属性名称

var

false

String

用于指定将格式化结果保存到某个Web域中的某个属性的名称

scope

false

String

指定将格式化结果保存到哪个Web域中

<fmt:message>标签有如下一些特性:

  1. 如果指定的资源不存在,输出“???<key>???”形式的错误信息。
  2. 如果ResouceBundle中不存在key属性指定的信息,输出“???<key>???”形式的错误信息。
  3. 如果key属性的值为null或空字符串,输出“??????”形式的错误信息。
  4. 如果没有指定var属性,<fmt:message>标签将直接输出格式化的结果;如果指定了var属性,则<fmt:message>标签将格式化的结果保存在scope属性指定的Web域中,保存在Web域中的属性名称为var属性指定的值。如果指定了scope属性,就必须指定var属性。
  1. 要输出的消息的关键字可以使用key属性指定,也可以在标签体中指定。如果<fmt:message>标签嵌套在<fmt:bundle>标签内,并且<fmt:bundle>标签设置了prefix属性,则该属性的值追加到<fmt:message>标签指定的关键字的前面。
  2. 可以通过在<fmt:message>标签内嵌套<fmt:param>标签来指定格式化文本串中的占位符的参数值,每个<fmt:param>标签分别设置一个参数,各个<fmt:param>标签的顺序与格式化字符串的参数相对应,即第一个<fmt:param>标签对应于格式化字符串中的第一个参数,第二个<fmt:param>对应于格式化字符串中的第二个参数,依次类推。
  3. 如果<fmt:message>标签处理的格式化文本串中包含有参数,但其中没有嵌套与该参数对应<fmt:param>标签,则直接输出该参数在格式化文本串中的原始形式。

 

例程8-30是一个使用<fmt:message>标签的例子程序。

例程8-30  fmt_message.jsp

 

 

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

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

<jsp:useBean id="now" class="java.util.Date" />

<%

       session.setAttribute("number", new Integer(8888888));

%>

<fmt:setBundle basename="org.it315.greetings" var="greetings" />

&lt;fmt:message&gt;标签内嵌套&lt;fmt:param&gt;标签的情况:<br />

<fmt:message bundle="${greetings}">

       org.it315.okKey

       <fmt:param value="${now}" />

       <fmt:param value="${number}" />

</fmt:message><br /><hr />

&lt;fmt:message&gt;标签内没有嵌套&lt;fmt:param&gt;标签的情况:<br />

<fmt:bundle basename="org.it315.greetings" prefix="org.it315.">

       <fmt:message key="okKey" />

</fmt:bundle>     

 

 

例程8-30的运行结果如图8.25 所示。

 

8.25

8.4.6  <fmt:param>标签

<fmt:param>标签用于为格式化文本串中的占位符设置参数值,它只能嵌套在<fmt:message>标签内使用。<fmt:param>标签有如下两种语法格式:

语法1,用value属性指定参数值:

<fmt:param value="messageParameter" />

语法2,在标签体中指定参数的值的情况:

<fmt:param>

    body content

</fmt:param>

说明:<fmt:param>标签的value属性的类型是java.lang.Object,它支持动态属性值。在前面的例程中已经多次使用了<fmt:param>标签,这里就不再对<fmt:param>标签进行举例讲解了。

8.4.7  <fmt:requestEncoding>标签

<fmt:requestEncoding>标签用于设置请求消息的字符集编码,该标签内部调用request.setCharacterEncoding()方法,以便Web容器将请求消息中的参数值按该字符集编码转换成Unicode字符串返回。<fmt:requestEncoding>标签的语法格式如下:

    <fmt:requestEncoding [value="charsetName" />

其中的value属性用于指定请求消息的字符集编码,其类型为String,支持动态属性值。

关于<fmt:requestEncoding>标签的几点说明:

1)为了能够正确解码请求参数值中的非ISO-8859-1编码的字符,应该调用这个标签来设置请求消息的字符集编码,并且必须在获取任何请求参数(包括使用EL表达式获取参数)之前进行调用。

2)因为很多浏览器没有完全遵守HTTP规范,在请求消息中没有包含Content-Type请求头,所以使用这个标签来设置请求消息的字符集编码是很有必要的。

3<fmt:requestEncoding>标签的value属性的设置值通常为提交这次请求的页面的字符集编码,如果不能预先确定请求消息的字符集编码,那就不应指定value属性。如果没有设置<fmt:requestEncoding>标签的value属性,<fmt:requestEncoding>标签首先采用请求消息的Content-Type头中定义的字符集编码,如果不能从请求消息的Content-Type头中确定字符集编码,则采用session域中的javax.servlet.jsp.jstl.fmt.request.charset属性的值,否则,采用ISO-8859-1字符集编码。

例程8-31是一个使用<fmt:requestEncoding>标签的例子程序。

例程8-31 fmt_requestEncoding.jsp

 

 

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

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

<fmt:requestEncoding value="gb2312" />

    我最喜欢的科目是:${param.favourite}<br />

<form method="POST">

    你最喜欢学习的是什么科目?<br />

    <input type="text" name="favourite" />

    <input type="submit" value="OK" /><br/>

</form>

 

 

例程8-31的运行结果如图8.26所示,在文本框中输入“英语”,单击其中的OK按钮后的运行结果如图8.27所示

 

8.26

 

8.27

8.4.8  <fmt:timeZone>标签

<fmt:timeZone>标签用于设置时区,但它的设置值只对其标签体部分有效。<fmt:timeZone>标签标签的语法格式如下:

<fmt:timeZone value="timeZone">

       body content

</fmt:timeZone>

其中的value属性支持动态属性值,它的值可以是一个命名时区的字符串,也可以是java.util.TimeZone类的一个实例对象。如果value属性的值为null或空字符串,标签体中的内容就使用GMT0基准时区。如果value属性的值是表示时区名称的字符串,这个字符串通过java.util.TimeZone.getTimeZone()静态方法被解析为java.util.TimeZone类的实例对象

例程8-32是一个使用<fmt:timeZone>标签的例子程序。

例程8-32 fmt_timeZone.jsp

 

 

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

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

<%@ page import="java.util.TimeZone" %>

<jsp:useBean id="now" class="java.util.Date" />

使用“GMT+1:00”时区:<br />

<fmt:timeZone value="GMT+1:00">

       <fmt:formatDate value="${now}" type="both" dateStyle="full" timeStyle="full"/>

</fmt:timeZone><br /><hr />

<%

       TimeZone tz = TimeZone.getDefault();

       pageContext.setAttribute("tz", tz);

%>

使用默认的时区,valuejava.util.TimeZone的一个实例:<br />

<fmt:timeZone value="${tz}">

       <fmt:formatDate value="${now}" type="both" dateStyle="full" timeStyle="full"/>

</fmt:timeZone><br /><hr />

使用“America/Los_Angeles”时区:<br />

<fmt:timeZone value="America/Los_Angeles">

       <fmt:formatDate value="${now}" type="both" dateStyle="full" timeStyle="full"/>

</fmt:timeZone>

 

 

8-32运行结果如图8.28 所示。

 

8.28

8.4.9  <fmt:setTimeZone>

<fmt:setTimeZone>标签用于在JSP页面中显示地设置时区,并将设置的时区信息以TimeZone对象的形式保存在某个Web域中,其在Web域中的属性名称为javax.servlet.jsp.jstl.fmt.timeZone<fmt:setTimeZone>标签的语法格式如下:

<fmt:setTimeZone value="timeZone"

                [var="varName"]

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

<fmt:setTimeZone>标签的属性说明如表8.16所示。

表8.16  <fmt:setTimeZone>标签的属性

属性名

是否支持EL

属性类型

value

true

String或java.util.TimeZone

指定表示时区的ID字符串或TimeZone对象

var

false

String

指定将创建出的TimeZone实例对象保存到Web域中的属性名称

scope

false

String

指定将创建出的TimeZone实例对象保存到哪个Web域中

其中,value属性的设置值的细节与<fmt:timeZone>标签的细节相同;<fmt:setTimeZone>标签将创建出的TimeZone实例对象保存在scope属性指定的Web域中,如果指定了var属性,保存在Web域中的属性名称为var属性指定的值,否则,保存在Web域中的属性名称为javax.servlet.jsp.jstl.fmt.timeZone,所有没有嵌套在其他<fmt:timeZone>标签中且未指定timezone属性的<fmt:formatDate>标签都将使用该域属性名关联的时区。

例程8-33是一个使<fmt:setTimeZone>标签的例子程序

例程8-33 fmt_setTimeZone.jsp

 

 

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

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

<jsp:useBean id="now" class="java.util.Date" />

<fmt:setTimeZone value="America/Los_Angeles"/>

America/Los_Angeles”时区的现在时间是:<br />

<fmt:formatDate value="${now}" type="both" /><hr />

GMT+1:00”时区的现在时间是:<br />

<fmt:timeZone value="GMT+1:00">

       <fmt:formatDate value="${now}" type="both" />

</fmt:timeZone>

 

 

例程8-33的运行结果如图8.29所示。

 

8.29

8.4.10  <fmt:formatDate>标签

<fmt:formatDate>标签用于对日期和时间按本地化信息进行格式化,或对日期和时间按化为JSP页面作者自定义的格式进行格式化。<fmt:formatDate>标签的语法格式如下:

<fmt:formatDate value="date"   

                [type="{time|date|both}"]

                [dateStyle="{default|short|medium|long|full}"]

                [timeStyle="{default|short|medium|long|full}"]

                [pattern="customPattern"]

                [timeZone="timeZone"]

                [var="varName"]

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

<fmt:formatDate>标签的属性说明如表8.17所示。

表8.17 <fmt:formatDate>标签的属性

属性名

是否支持EL

属性类型

value

true

java.util.Date

指定要格式化的日期或时间

type

true

String

指定是格式化输出日期部分,还是格式化输出时间部分,还是两者都输出

dateStyle

true

String

指定日期部分的输出格式,其可用的设置值可以参照java.text.DateFormat类的讲解。该属性仅在type属性取值为date或both时才有效

timeStyle

true

String

指定时间部分的输出格式,其可用的设置值请参照java.text.DateFormat类的讲解。该属性仅在type属性取值为time或both时才有效

pattern

true

String

指定一个自定义的日期和时间输出格式

timeZone

true

String或java.util.timeZone

指定当前采用的时区

var

false

String

用于指定将格式化结果保存到某个Web域中的某个属性的名称

scope

false

String

指定将格式化结果保存到哪个Web域中

 

<fmt:formatDate>标签有如下一些特性:

  1. 如果没有指定var属性,<fmt: formatDate>标签将直接输出格式化的结果;如果指定了var属性,则<fmt:formatDate>标签将格式化的结果保存在scope属性指定的Web域中,保存在Web域中的属性名称为var属性指定的值。如果指定了scope属性,就必须指定var属性。
  1. 如果value属性的值为null,则不输出任何内容,如果这时候指定了var和scope属性,将scope属性指定的Web域中的var属性指定的域属性删除。
  1. JSP网页作者可以使用pattern属性设置自定义的日期格式,这时候,<fmt:formatDate>标签将忽略type、dateStyle和timeStyle等属性。pattern属性的值必须符合java.text.SimpleDateFormat类的日期模式的语法。
  2. 如果timeZone属性的值为null或者空字符串,相当于没有设置这个属性。
  3. 如果要格式化一个表示日期或时间的字符串,必须首先用<fmt:parseDate>标签将其解析为java.util.Date类的一个实例对象,再使用<fmt:formatDate >标签对这个Date实例对象进行格式化。<fmt:parseDate>标签的介绍请参看8.4.11节。
  4. 如果<fmt:formatDate>标签不能确定格式化的本地化信息,就使用java.util.Date.toString()方法作为输出格式。

例程8-34是一个使用<fmt:formatDate>标签的例子程序。

例程8-34 fmt_formatDate.jsp

 

 

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

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

<%@ page import="java.util.TimeZone" %>

<jsp:useBean id="now" class="java.util.Date" />

格式化当前日期、时间:

<fmt:formatDate value="${now}" type="both" timeStyle="medium" dateStyle="long"/>

<hr/>

<%

       session.setAttribute("tz", TimeZone.getTimeZone("GMT+10"));

%>

指定时区为“GMT+10”:

<fmt:formatDate value="${now}" type="both" timeZone="${tz}" /><hr />

指定自定义的格式,月..:

<fmt:formatDate value="${now}" pattern="MM.dd.yyyy" /><hr />

格式化用字符串表示的日期:

<fmt:parseDate value="7/31/05" pattern="MM/dd/yy" var="parsed" />

<fmt:formatDate value="${parsed}" />

 

 

例程8-34运行结果如图8.30 所示。

 

8.30

8.4.11  <fmt:parseDate>标签

<fmt:parseDate>标签与<fmt: formatDate >标签的作用正好相反,它用于将一个表示日期和时间的字符串解析成java.util.Date实例对象,它有如下两种语法格式:

语法1,没有标签体的情况:

<fmt:parseDate value="dateString"

                [type="time|date|both"]

                [dateStyle="default|short|medium|long|full"]

                [timeStyle="default|short|medium|long|full"]

                [pattern="customPattern"]

                [timeZone="timeZone"]

                [parseLocale="parseLocale"]

                [var="varName"]

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

语法2, 有标签体的情况,在标签体中指定要被解析的日期和/或时间值:

<fmt:parseDate [type="time|date|both"]

                [dateStyle="default|short|medium|long|full"]

                [timeStyle="default|short|medium|long|full"]

                [pattern="customPattern"]

                [timeZone="timeZone"]

                [parseLocale="parseLocale"]

                [var="varName"]

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

    date value to be parsed

</fmt:parseDate>

<fmt:parseDate>标签的属性说明如表8.18所示。

表8.18 <fmt:parseDate>标签的属性

 

<fmt:parseDate>标签有如下一些特性

  1. value属性的值必须是合法的日期/时间字符串,否则<fmt:parseDate>标签在解析时抛出异常。如果value属性的值为null或空字符串,就删除scope属性指定的域范围中的var属性命名的变量。
  2. 根据type属性的值,要解析日期字符串可以包含日期、时间或同时包含日期和时间,并根据指定的格式类型进行解析。
  3. 如果timeZone属性的值为null或空字符串,按没有设置这个属性处理。
  4. 如果parseLocale属性的值为null或空字符串,按没有设置该属性处理。
  1. 如果指定pattern属性,标签就忽略它的type、dateStyle和timeStyle属性。这时,根据pattern属性设置的自定义格式解析value属性指定的日期和/或时间字符串,所以,要解析的日期和/或时间字符串必须严格符合pattern属性设置的自定义格式。pattern属性的值必须符合java.text.SimpleDateFormat类的模式语法。
  2. 如果标签不能确定解析的本地环境,就抛出JspException异常,异常信息中包含要解析的字符串。

 

在前面的例程8-34中已经使用了<fmt:parseDate>标签,这里就不再对<fmt:parseDate>标签进行举例讲解了。

8.4.12  <fmt:formatNumber>标签

<fmt:formatNumber>标签用于将数值、货币或百分数按本地化信息进行格式化,或者按JSP页面作者自定义的格式进行格式化。<fmt:formatNumber>标签有两种语法格式:

语法1,没有标签体的情况:

<fmt:formatNumber value="numericValue"

                 [type="{number|currency|percent}"]

                 [pattern="customPattern"]

                 [currencyCode="currencyCode"]

                 [currencySymbol="currencySymbol"]

                 [groupingUsed="{true|false}"]

                 [maxIntegerDigits="maxIntegerDigits"]

                 [minIntegerDigits="minIntegerDigits"]

                 [maxFractionDigits="maxFractionDigits"]

                 [minFractionDigits="minFractionDigits"]

                 [var="varName"]

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

语法2,有标签体的情况,在标签体中指定要被格式化的数值:

<fmt:formatNumber [type="{number|currency|percent}"]

                 [pattern="customPattern"]

                 [currencyCode="currencyCode"]

                 [currencySymbol="currencySymbol"]

                 [groupingUsed="{true|false}"]

                 [maxIntegerDigits="maxIntegerDigits"]

                 [minIntegerDigits="minIntegerDigits"]

                 [maxFractionDigits="maxFractionDigits"]

                 [minFractionDigits="minFractionDigits"]

                 [var="varName"]

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

        要被格式化的数值

</fmt:formatNumber>

如果指定scope属性,就必须指定var属性。<fmt:formatNumber>标签的属性说明如表8.19所示。

8.19 <fmt:formatNumber>标签的属性

 

<fmt:formatNumber>标签有如下一些特性

  1. 如果value属性的值为null,则不输出任何内容,如果这时候指定了var和scope属性,将scope属性指定的Web域中的var属性指定的域属性删除。
  2. pattern属性优先于type属性,不管是使用哪个属性,格式化的标记(例如十进制小数的整数部分、小数部分的分隔符及组分隔符)通过格式化的Locale特性来决定。
  3. pattern属性指定的模式字符串必须符合java.text.DecimalFormat类的模式语法。如果pattern属性为null或空字符串,就相当于没有设置这个属性的值。
  4. currencyCode属性的值必须是一个合法的ISO 4217货币编码。
  5. groupingUsed属性指定是否要将格式化后的数值的整数部分的数字分组。例如,在英语本地环境中,将比较大的数值按每三个数字分为一组,每组用逗号定界,其它的本地环境可能用点或空格来定界这样的分组。详细信息参考java.text.NumberFormat类的setGroupingUsed()方法。
  6. 当type属性的值为currency时,currencyCode属性用来显式地指定要显示的数据的货币符号。currencySymbol属性也可以显式地指定货币符号。当WEB容器的运行环境是JDK1.4或更高版本时,如果同时指定<fmt:formatNumber>标签的currencyCode和currencySymbol属性,前者优先于后者;但当WEB容器的运行环境是JDK1.4以前的版本时,currencySymbol属性优先于currencyCode属性。
  7. 如果<fmt:formatNumber>标签不能确定格式化的本地环境,就使用Number.toString()作为输出格式。

例程8-35是一个使用<fmt:formatNumber>标签的例子程序。

例程8-35  fmt_formatNumber.jsp

 

 

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

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

<%@ page import="java.util.Currency,java.util.Locale"%>

将数值格式化为货币格式:

<fmt:formatNumber value="9876543.21" type="currency" /><hr />

将数值格式化为百分数格式:

<fmt:formatNumber value="12.3" type="percent" /><hr />

将数值格式化为自定义的格式:

<fmt:formatNumber value="12.3" pattern=".000" />

<fmt:formatNumber value="123456.7891" pattern="#,#00.0#" /><hr />

<%

       String code = Currency.getInstance(Locale.CHINA).getCurrencyCode();

       String symbol = Currency.getInstance(Locale.US).getSymbol();

       session.setAttribute("code", code);

       session.setAttribute("symbol", symbol);

%>

同时指定currencyCodecurrencySymbol属性,前者优先于后者:

<fmt:formatNumber value="1234567.11" type="currency" currencyCode="${code}"

       currencySymbol="${symbol}" />

 

 

例程8-35的运行结果如图8.31 所示。

 

8.31

8.4.13  <fmt:parseNumber>标签

<fmt:parseDate>标签与<fmt: formatNumber >标签的作用正好相反,它用于将一个按本地化方式被格式化后的数值、货币或百分数解析为数值,它有如下两种语法格式:

语法1,没有标签体的情况:

<fmt:parseNumber value="numericValue"  

                [type="{number|currency|percent}"]

                [pattern="customPattern"]

                [parseLocale="parseLocale"]

                [integerOnly="{true|false}"]

                [var="varName"]

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

语法2,有标签体的情况,在标签体中指定要被解析的数值:

<fmt:parseNumber [type="{number|currency|percent}"]

                 [pattern="customPattern"]

                 [parseLocale="parseLocale"]

                 [integerOnly="{true|false}"]

                 [var="varName"]

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

    numeric value to be parsed

</fmt:parseNumber>

<fmt:parseNumber>标签的属性说明如表8.20所示。

表8.20 <fmt:parseNumber>标签的属性

 

<fmt:parseNumber>标签有如下一些特性

  1. 如果value属性的值为null或空字符串,则将scope属性指定的Web域中的var属性指定的域属性删除。如果value属性的值不能解析为数值就抛出异常。
  2. pattern属性指定的模式字符串必须符合java.text.DecimalFormat类的模式语法。如果其值为null或空字符串,就相当于没有设置这个属性的值。使用<fmt:parseNumber>解析值时要特别注意,它执行的解析非常严格,要解析的数值字符串必须严格符合特定的本地环境及pattern属性设置的自定义格式。
  3. 如果parseLocale属性的值为null或空字符串,就相当于没有指定该属性。
  4. integerOnly属性指定是否只是解析value的整数部分。详细信息请看java.text.NumberFormat接口的setParseIntegerOnly()方法。
  5. 如果同时指定type属性和pattern属性,后者优先权高于前者。
  6. 如果设置了var属性,将解析的结果保存在某个Web域中的var属性指定的域属性中;否则,<fmt:parseNumber>将使用java.lang.Number.toString()直接输出解析的结果。如果指定scope属性,则必须指定var属性。
  7. 如果标签不能确定解析的本地环境,就抛出JspException异常,异常信息中包含要解析的字符串。

例程8-36是一个使用<fmt:parseNumber>标签的例子程序。

例程8-36 fmt_parseNumber.jsp

 

 

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

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

本地环境为“en_US”解析字符串“$123,456,789.00”

<fmt:parseNumber value="$123,456,789.00" type="currency" parseLocale="en_US"/>

<hr/>

解析整个数值字符串“123,456,789%”:

<fmt:parseNumber value="123,456,789%" type="percent" /><hr />

只解析“123,456,789%”的整数部分:

<fmt:parseNumber value="123,456,789%" type="percent" integerOnly="true"/><hr />

同时设置typepattern属性,后者优先于前者:

<fmt:parseNumber value="12.3" type="currency" pattern=".000" />

 

 

例程8-36运行结果如图8.32 所示。

 

8.32

 

注意:在分层设计的软件架构中,JSP页面通常仅用于实现系统的表示层,数据解析的任务不适合在表示层进行处理,而最好是业务逻辑层进行处理,所以,为了增强软件的易维护性和可扩展性,读者应尽量避免在JSP 页面中使用<fmt:parseDate>和<fmt:parseNumber>标签。

 

8.39

8.7  JSTL函数

为了简化在JSP页面操作字符串,JSTL中提供了一套EL自定义函数,这些函数包含了JSP页面制作者经常要用到的字符串操作。例如,fn:toLowerCase将字符串中的字符变为小写,fn:indexOf返回一个指定字符串在另一个字符串中第一次出现的索引位置。

JSTL中提供的EL自定义函数必须在EL表达式中使用,例如,${fn:toUpperCase("www.it315.org")}

8.7.1  fn:toLowerCase函数

fn:toLowerCase函数将一个字符串中包含的所有字符转换为小写形式,并返回转换后的字符串,它接收一个字符串类型的参数。

fn:toLowerCase函数的应用举例:

  1. fn:toLowerCase("Www.IT315.org") 的返回值为字符串“www.it315.org
  2. fn:toLowerCase("")的返回值为空字符串

8.7.2  fn:toUpperCase函数

fn:toUpperCase函数将一个字符串中包含的所有字符转换为大写形式,并返回转换后的字符串,它接收一个字符串类型的参数。

fn:toUpperCase函数的应用举例:

  1. fn:toUpperCase("Www.IT315.org") 的返回值为字符串“WWW.IT315.ORG
  2. fn:toUpperCase("")的返回值为空字符串

8.7.3  fn:trim函数

fn:trim函数删除一个字符串的首尾的空格,并返回删除空格后的结果字符串,它接收一个字符串类型的参数。需要注意的是,fn:trim函数不能删除字符串中间位置的空格。

例如fn:trim("   www.it  315.org  ") 的返回值为字符串www.it  315.org

8.7.4  fn:escapeXml函数

fn:escapeXml函数将字符串中的需要进行转义的HTML特殊字符按表8.2进行HTML编码转换,并返回转换后的字符串这样就可以在浏览器中显示出HTML特殊字符。fn:escapeXml函数接收一个字符串类型的参数。

例如,运行包含如下代码的JSP文件后浏览器将跳转到“http://www.it315.org页面,这是因为这些代码被浏览器作为HTML标签解释执行:

<meta http-equiv="refresh" content="0;url=http://www.it315.org"/>

如果用fn:escapeXml函数处理上面的代码运行JSP文件后就在浏览器窗口输出这行代码例程8-54是使用fn:escapeXml函数处理上面的代码的例子程序。

例程8-54  fn_escapeXml.jsp

 

 

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

${fn:escapeXml('<meta http-equiv="refresh"

              content="0;url=http://www.it315.org"/>')}

 

 

fn_escapeXml.jsp页面的运行结果如图8.46所示。

 

8.46

8.7.5  fn:length函数

fn:length函数用于返回一个集合或数组对象中包含的元素的个数,或返回一个字符串中包含的字符的个数,返回值为int类型。fn:length函数接收一个参数,这个参数可以是<c:forEach>标签的items属性支持的任何类型,包括任意类型的数组、java.util.Collection、java.util.Iterator、java.util.Enumeration、java.util.Map等类的实例对象和字符串。

如果fn:length函数的参数为null或者是元素个数为0的集合或数组对象,则函数返回0;如果参数是空字符串,则函数返回0。例程8-55是使用fn:length函数的实例程序。

例程8-55  fn_length.jsp

 

 

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

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

<%@ page import="java.util.*, org.it315.UserBean" %>

<%

       Collection users = new ArrayList();

       for(int i=0; i<5; i++)

       {

              UserBean user = new UserBean();

              user.setUserName("user" + i);

              user.setPassword("guest" + i);

              users.add(user);

       }

       session.setAttribute("users", users);

%>

There are ${fn:length(users)} users online.<hr />

字符串“www.it315.org”中包含 ${fn:length("www.it315.org")} 个字符

 

 

fn_length.jsp页面的运行结果如图8.47所示。

 

图8.47

8.7.6  fn:split函数

fn:split函数以指定字符串作为分隔符,将一个字符串分割成字符串数组并返回这个字符串数组。fn:split函数接收两个字符串类型的参数,第一个参数表示要分割的字符串,第二个参数表示作为分隔符的字符串。

  1. fn:split函数实现java.util.StringTokenizer类分割字符串的操作,得到的字符串数组中的各字符串元素不包含分隔符本身,例如,fn:split("www.it315.org", ".")[1]的返回值为字符串“it315
  1. 如果第一个参数是空字符串,则fn:split函数返回的字符串数组中只包含空字符串这一个元素,例如,fn:split("", ".")[0]的返回值为空字符串。
  2. 如果第二个参数是空字符串,则函数返回的字符串数组中只包含源字符串这一个元素,例如,fn:split("www.it315.org", "")[0]的返回值为字符串“www.it315.org”,fn:split("www.it315.org", "")[1]的返回值为空字符串。

8.7.7  fn:join函数

fn:join函数以一个字符串作为分隔符,将一个字符串数组中的所有元素合并为一个字符串并返回合并后的结果字符串。fn:join函数接收两个参数,第一个参数是要操作的字符串数组,第二个参数是作为分隔符的字符串。

如果fn:join函数的第二个参数是空字符串,则fn:join函数的返回值是不使用任何分隔符将字符串数组中的各元素连接起来的结果字符串。

fn:join函数的应用举例:

  1. 假设stringArray是保存在Web域中的一个属性,它表示一个值为{"www","it315","org"}的字符串数组,则fn:join(stringArray, ".")返回字符串“www.it315.org
  2. fn:join(fn:split("www,it315,org", ","), ".") 的返回值为字符串“www.it315.org

8.7.8  fn:indexOf函数

fn:indexOf函数返回指定字符串在一个字符串中第一次出现的索引值,返回值为int类型。fn:indexOf函数接收两个字符串类型的参数,如果第一个参数字符串中包含第二个参数字符串,那么,不管第二个参数字符串在第一个参数字符串中出现几次,fn:indexOf函数总是返回第一次出现的索引值;如果第一个参数中不包含第二个参数,则fn:indexOf函数返回-1。如果第二个参数为空字符串,则fn:indexOf函数总是返回0

fn:indexOf函数的应用举例:

  1. fn:indexOf("www.it315.org","t3") 的返回值为5
  2. fn:indexOf("www.it315.org","aaa") 的返回值为 -1
  3. fn:indexOf("www.it315.org","") 的返回值为0

8.7.9  fn:contains函数

fn:contains函数检测一个字符串中是否包含指定的字符串,返回值为布尔类型。fn:contains函数在比较两个字符串是否相等时是大小写敏感的。

fn:contains函数接收两个字符串类型的参数,如果第一个参数字符串中包含第二个参数字符串,则fn:contains函数返回true,否则返回false。如果第二个参数的值为空字符串,则fn:contains函数总是返回true。实际上,fn:contains(string, substring)等价于fn:indexOf(string, substring) != -1

fn:contains函数的应用举例:

  1. fn:contains("www.it315.org", "org")的返回值为true
  2. fn:contains("www.it315.org", "Org")的返回值为false
  3. fn:contains("", "")的返回值为true

8.7.10  fn:containsIgnoreCase函数

fn:containsIgnoreCase函数检测一个字符串中是否包含指定的字符串,在比较两个字符串是否相等时大小写不敏感。fn:containsIgnoreCase函数的参数和返回值的类型都与fn:contains函数相同,只是前者在比较两个字符串是否相等时不考虑字符的大小写,而后者考虑字符的大小写,fn:containsIgnoreCase(string, substring)等价于fn:contains(fn:toUpperCase(string),fn:toUpperCase(substring))

fn:containsIgnoreCase函数的应用举例:

  1. fn:containsIgnoreCase("www.it315.org", "Org") 的返回值为 true
  2. fn:containsIgnoreCase (fn:toUpperCase("www.it315.org"), fn:toUpperCase("Org"))的返回值为true

8.7.11  fn:startsWith函数

fn:startsWith函数用于检测一个字符串是否是以指定字符串开始的,返回值为布尔类型。

fn:startsWith函数接收两个字符串类型的参数,如果第一个参数字符串以第二个参数字符串开始,则函数返回true,否则函数返回false。如果第二个参数为空字符串,则fn:startsWith函数总是返回true。实际上,fn:startsWith(string, prefix)等价于表达式fn:indexOf(string, prefix) == 0。

fn:startsWith函数的应用举例:

  1. fn:startsWith("www.it315.org","it315")的返回值为false
  2. fn:startsWith("www.it315.org","www")的返回值为true
  3. fn:startsWith("www.it315.org","")的返回值为true

8.7.12  fn:endsWith函数

fn:endsWith函数用于检测一个字符串是否是以指定字符串结束的,返回值为布尔类型。

fn:endsWith函数接收两个字符串类型的参数,如果第一个参数字符串以第二个参数字符串结束,则函数返回true,否则函数返回false。如果第二个参数为空字符串,则fn:endsWith函数总是返回true

fn:endsWith函数的应用举例:

  1. fn:endsWith("www.it315.org","it315")的返回值为false
  2. fn:endsWith("www.it315.org","org")的返回值为true
  3. fn:endsWith("www.it315.org","")的返回值为true

8.7.13  fn:replace函数

fn:replace函数将一个字符串中包含的指定子字符串替换为其它的指定字符串,并返回替换后的结果字符串。fn:replace方法接收三个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示源字符串中要被替换的子字符串,第三个参数表示要被替换成的字符串。

fn:replace函数按照下面的说明执行字符串的替换操作:

(1)源字符串中所有的第二个参数指定的子字符串都被替换成第三个参数指定的字符串,如果替换后的结果字符串中包含第二个参数指定的子字符串,则不再进行替换。例如,

  1. fn:replace("www it315 org", " ", ".")的返回值为字符串“www.it315.org”
  2. fn:replace("ddabccff", "abc", "ab")的返回值为字符串“ddabcff”,这个字符串中还包含字符串“abc”,但不再执行替换操作

(2)如果第一个参数是空字符串,则fn:replace函数返回空字符串,例如,fn:replace("", " ", ".")的返回值为空字符串。

(3)如果第二个参数是空字符串,则fn:replace函数不执行任何替换操作,返回第一个参数指定的字符串。例如,fn:replace("www.it315.org", "", " ") 的返回值为字符串“www.it315.org”。

(4)如果第三个参数是空字符串,则fn:replace函数删除源字符串中包含的第二个参数指定的子字符串。例如,fn:replace("www.it315.org", ".", "")的返回值为字符串“wwwit315org”。

8.7.14  fn:substring函数

fn:substring函数用于截取一个字符串的子字符串并返回截取到的子字符串。fn:substring函数接收三个参数,第一个参数是用于指定要操作的源字符串,第二个参数是用于指定截取子字符串开始的索引值,第三个参数是用于指定截取子字符串结束的索引值,第二个参数和第三个参数都是int类型,其值都从0开始。

fn:substring函数按照下面的说明截取一个字符串的子字符串:

  1. 如果第二个参数的值大于或等于源字符串中包含的字符个数,则fn:substring函数返回空字符串;
  2. 如果第二个参数的值小于0,则将其值设置为0;
  3. 如果第三个参数的值小于0或大于源字符串中包含的字符个数,则截取到源字符串的结尾的字符;
  4. 如果第三个参数的值小于第二个参数的值,则fn:substring函数返回空字符串。

fn:substring函数的应用举例(字符串“www.it315.org”中包含13个字符):

  1. fn:substring("www.it315.org", 4, 9) 的返回值为字符串“it315”
  2. fn:substring("www.it315.org", 13, 14) 返回空字符串
  3. fn:substring("www.it315.org", -1, 9) 的返回值为字符串“www.it315”
  4. fn:substring("www.it315.org", 4, -1) 的返回值为字符串“it315.org
  5. fn:substring("www.it315.org", 4, 13) 的返回值为字符串“it315.org
  6. fn:substring("www.it315.org", 4, 3) 返回空字符串

8.7.15  fn:substringAfter函数

fn:substringAfter函数用于截取并返回一个字符串中的指定子字符串第一次出现之后的子字符串。fn:substringAfter函数接收两个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示指定的子字符串,例如,fn:substringAfter("www.it315.org", ".")的返回值为字符串“it315.org”。

fn:substringAfter函数按照下面的说明截取一个字符串的子字符串:

  1. 如果第一个参数是空字符串,则fn:substringAfter函数返回空字符串,例如fn:substringAfter("", ".") 的返回值为空字符串;
  2. 如果第二个参数是空字符串,则fn:substringAfter函数的返回值为源字符串,例如,fn:substringAfter("www.it315.org", "")的返回值为字符串“www.it315.org”;
  3. 如果第二个参数不是第一个参数的子字符串,则substringAfter函数返回空字符串,例如,fn:substringAfter("www.it315.org", "itorg")的返回值为空字符串。

8.7.16  fn:substringBefore函数

fn:substringBefore函数用于截取并返回一个字符串中的指定子字符串第一次出现之前的子字符串。fn:substringBefore函数接收两个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示指定的子字符串,例如,fn:substringBefore("www.it315.org", ".") 的返回值为字符串“www”。

fn:substringBefore函数按照下面的说明截取一个字符串的子字符串:

  1. 如果第一个参数是空字符串,则fn:substringBefore函数返回空字符串,例如,fn:substringBefore("", ".") 的返回值为空字符串;
  2. 如果第二个参数是空字符串,则fn:substringBefore函数返回空字符串,例如,fn:substringBefore("www.it315.org", "") 的返回值为空字符串
  3. 如果第二个参数不是第一个参数的子字符串,则fn:substringBefore函数返回空字符串,例如,fn:substringBefore("www.it315.org", "itorg") 的返回值为空字符串。

 

 

思考题:

 

1、请用<c:forEach>标签在一个table表格中列表显示出一个集合对象中的所有元素,奇偶行用明暗相间的颜色进行分隔。

 

2、请用<c:forEach>标签将一个整数数组中所有元素用逗号分隔成“1,2,3”的形式进行输出,注意,最后一个元素后面没有逗号。

 

3、请嵌套调用<c:forTokens>标签打印出http://www.it315.org/deal.jsp?name=zxx&password=123中的每个参数名和参数值。

 

4 请在EL表达式用JSTL函数fn:splitfn:length取出字符串"a/b/c/d.txt"中的最后一个"/"后面的内容。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Toroidals

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值