7.3.2 include指令

tag file中的include指令和JSP页面中的include指令是一样的。可以使用这个指令来将外部文件导入到tag file。当你有一个公共资源文件有可能用在多个tag file中时,include指令将能够发挥它的作用。这个公共资源文件可以是静态文件(例如HTML文件),也可以是动态文件(例如其他tag file)。

实例

includeDemoTag.tag

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<body>
<h2>下面是标签文件includeDemoTag.tag中的内容</h2>
<hr color="green">
<!-- 引入自定义标签目录,取个别名为前缀`easy` -->
<%@ taglib prefix="easy" tagdir="/WEB-INF/tags"%>
<!-- 引入前缀`easy`所表示的标签目录中的`includeDemoTag.tag`文件 -->
<easy:includeDemoTag />
<hr color="green">
</body>
</html>

includeDemoTag.tag

1
2
3
4
5
6
7
8
9
10
<!-- 设置tag文件的编码 -->
<%@ tag pageEncoding="utf-8"%>
<strong>以下是included2.tagf的内容:</strong>
<hr color="red">
<%@ include file="included2.tagf"%>
<hr color="red">
<hr color="blue">
<strong>下面是included.tagf文件中的内容:</strong>
<%@ include file="included.tagf"%>
<hr color="blue">

included2.tagf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@tag pageEncoding="utf-8"%>
<table>
<tr>
<th>水果</th>
<th>单价(斤)</th>
</tr>
<tr>
<td>苹果</td>
<td>4.8</td>
</tr>
<tr>
<td>香蕉</td>
<td>3.6</td>
</tr>
<tr>
<td>草莓</td>
<td>12.5</td>
</tr>
</table>

included.tagf

1
2
3
4
<%@ tag pageEncoding="utf-8"%>
<%
out.print("<strong>included.tagf文件中的内容</strong>");
%>

可以通过下面的URL来访问includeDemoTagTest.jsp页面:
http://localhost:8080/app07a/includeDemoTagTest.jsp
显示效果如下:
这里有一张图片
关于include指令更详细的介绍,可以查看第3章。

7.3.1 tag指令语法格式

tag指令和JSP页面中的page指令类似。以下是它的使用语法:

1
<%@ tag attribute1="value1" attribute2="value2" ... %>

tag指令的属性

下表中列出了tag指令的全部属性,这些属性都是非必须的

属性 描述
display-name XML工具中显示的名称。默认值是不包含后缀的tag file
body-content 指定标签body的类型,body-content属性值有emptytagdependentscriptless,默认值是scriptless
dynamic-attributes 指定tag file动态属性的名称。当dynamicattributes值被设定时,会产生一个Map来存放这些动态属性的名称和对应的值
small-icon 指定一个图片路径,用于在XML工具上显示小图标。一般不会用到
large-icon 指定一个图片路径,用于在XML工具上显示大图标。一般也不会用到
description 标签的描述信息
example 标签使用实例的描述
language tag file中使用的脚本语言类型。当前版本的JSP中,该值必须设为“java
import 用于导入一个java类型,和JSP页面中的import相同
pageEncoding 指定tag file使用的编码格式,可以使用“CHARSET”中的值。和JSP页面中的pageEncoding相同

除了import属性,其他所有的属性在一个tag指令或一个tag file中都只能出现一次。例如,以下的tag file就是无效的,因为body-content属性在同一个tag file中出现了多次:

1
2
<%@ tag display-name="first tag file" body-content="scriptless"%>
<%@ tag body-content="empty" %>

下面是一个有效的tag指令,尽管import属性出现了两次,但这是被允许的:

1
<%@ tag import="java.util.ArrayList" import="java.util.Iterator" %>

同理,下面的tag指令也是有效的:

1
2
<%@ tag body-content="empty" import="java.util.Enumeration" %>
<%@ tag import="java.sql.*" %>

7.3tag file指令

JSP页面一样,tag file可以使用指令来指挥JSP容器如何编译这个tag file

语法格式

tag file的指令语法和JSP是一样的:

1
<%@ directive attribute1="value1" attribute2="value2" ... %>

属性值必须被单引号或者双引号包裹<%@之后和%>之前的空格加不加都不影响正确性,但是为了可读性,建议加上空格。除了page指令,其他所有的JSP指令都可以用于tag file。在tag file中,可以使用tag指令代替page指令。另外,你还可以使用两个新指令:attribute variable

tag file中可以使用的指令

下表展示了所有可以在tag file中使用的指令。

指令 描述
tag 作用与JSP页面中的page指令类似
include 用于将其他资源导入tag file
taglib 用于将自定义标签库导入tag file
attribute 用于将自定义标签库导入tag file
variable 用于将自定义标签库导入tag file
接下来,我们会对这些指令分别进行介绍。

7.2 第一个tag file

这一节将用一个实例说明使用tag file是多么方便。下面的这个例子包含一个tag文件和一个使用这个tag文件的JSP页面。

项目结构

这个app07a项目结构如下图所示:
这里有一张图片
这个项目只有两个文件:

  • app07a/WebContent/WEB-INF/tags/firstTag.tag
  • app07a/WebContent/firstTagTest.jsp

创建tag file

firstTag.tag:

1
2
3
4
5
6
7
<%@ tag import="java.util.Date" import="java.text.DateFormat"%>
<%
DateFormat dateFormat =
DateFormat.getDateInstance(DateFormat.LONG);
Date now = new Date(System.currentTimeMillis());
out.println(dateFormat.format(now));
%>

tag fileJSP页面是很相似的。在firstTag.tag文件里包含了一个tag指令和一个脚本片段,其中tag指令里的两个import属性引入了脚本中需要使用的Java类。接下来,只需要将这个tag file放到WEB-INF/tags目录下就可以使用该tag file。注意**tag file名和标签的名字是一样的**,例如这个firstTag.tagtag file对应的标签名即为firstTag

JSP中使用tag file

firstTagTest.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>使用tag file</title>
</head>
<body>
<!-- tagdir属性表示该标签文件所在的目录,
prefix属性是该标签文件所在的目录的别名
后续将通过<前缀:文件名/>的方式引用标签文件
-->
<%@ taglib prefix="mytags" tagdir="/WEB-INF/tags"%>
Today is
<!-- 调用前缀mytags所表示的目录中的firstTag.tag这个文件
-->
<mytags:firstTag />
</body>
</html>

浏览器显示效果

可以用下面的链接访问firstTagTest.jsp来查看效果:http://localhost:8080/app07a/firstTagTest.jsp
内容如下所示:

1
Today is 2019年4月7日 

7.1 tagfile简介

tag file从两个方面简化了自定义标签的开发。

  • 首先,**tag file无须提前编译,直到第一次被调用才会被编译。除此之外,仅仅使用JSP语法就可以完成标签的扩展定义**,这意味着不懂Java的人也能够进行标签自定了。
  • 其次,标签库描述文件也不再需要了。原先需要在标签库描述文件里定义标签元素的名字,以及它所对应的action。使用tag file的方式,由于tag file名和action相同,因此不再需要标签库描述文件了。

JSP容器提供多种方式将tag file编译成Java的标签处理类。例如Tomcattag file翻译成继承于javax.servlet.jsp.tagext.SimpleTag接口的标签处理类。
一个tag fileJSP页面一样,它拥有指令、脚本、EL表达式、动作元素以及自定义的标签。一个tag filetagtagx为后缀,它们可以包含其他资源文件。一个被其他文件包含的tag file应该以tagf为后缀。
tag文件必须放在应用路径的WEB-INF/tags目录下才能生效。和标签处理类一样,tag 文件可以被打到jar包里。

tag file中可用的隐藏对象

tag file中也有一些隐藏对象,通过脚本或者EL表达式可以访问这些隐藏对象。这些隐藏对象和第3章中介绍的JSP隐藏对象类似。

对象 类型
request javax.servlet.http.HttpServletRequest
response javax.servlet.http.HttpServletResponse
out javax.servlet.jsp.JspWriter
session javax.servlet.http.HttpSession
application javax.servlet.ServletContext
config javax.servlet.ServletConfig
jspContext javax.servlet.jsp.JspContext

第7章 标签文件

在第6章中,我们介绍了自定义标签,通过写无脚本的JSP文件,可以促进分工,页面设计者可以和后台逻辑编码者同时进行工作。不过,编写自定义标签是一件冗长琐碎的事,你需要编写并编译一个标签处理类,还需要在标签库描述文件中定义标签。
JSP 2.0开始,通过tag file的方式,无须编写标签处理类和标签库描述文件,也能够自定义标签了tagfile在使用之前无须编译,并且不需要标签库定义文件。
本章会对tag file进行详细的说明。首先我们对tagfile进行介绍,然后会通过几个实例来详细了解如何通过tag file进行自定义标签。最后,还会介绍doBodyinvoke这两个动作元素。

5.8 小结

JSTL可以完成一般的任务(如遍历、集合和条件)、处理XML文档、格式化文本、访问数据库以及操作数据,等等。
本章介绍了比较重要的一些标签,如操作有界对象的标签(outsetremove)、执行条件测试的标签(ifchoosewhenotherwise)、遍历集合或token的标签(forEachforTokens)、解析和格式化日期与数字的标签(parseNumberformatNumberparseDateformatDate等),以及可以在EL表达式中使用的JSTL 1.2函数。

5.7 函数

除了定制行为外,JSTL 1.1JSTL 1.2还定义了一套可以在EL表达式中使用的标准函数。这些函数都集中放在function标签库中。为了使用这些函数,必须在JSP的最前面使用以下taglib指令:

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

调用函数时,要以下列格式使用一个EL

1
${fn:functionName}

这里的functionName是函数名。
大部分函数都用于字符串操作。例如,length函数用于字符串和集合,并返回集合或者数组中的项目数,或者返回一个字符串的字符数。

5.7.1 contains函数

contains函数用于测试一个字符串中是否包含指定的子字符串。如果字符串中包含该子字符串,则返回值为true,否则,返回false。其语法如下:

1
contains(string, substring).

实例

例如,下面这两个EL表达式都将返回true

1
2
3
4
<c:set var="myString" value="Hello World" />
<!-- 判断字符串有界变量`myString`中是否包含`Hello` -->
${fn:contains(myString, "Hello")}
<br> ${fn:contains("Stella Cadente", "Cadente")}

运行结果:

1
2
true
true

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>function Test</title>
</head>
<body>
<c:set var="myString" value="Hello World" />
<!-- 判断字符串有界变量`myString`中是否包含`Hello` -->
${fn:contains(myString, "Hello")}
<br> ${fn:contains("Stella Cadente", "Cadente")}
</body>
</html>

5.7.2 containsIgnoreCase函数

containsIgnoreCase函数与contains函数相似,但测试是区分大小写的,其语法如下:

1
containsIgnoreCase(string, substring)

例如,下列的EL表达式将返回true

1
${fn:containsIgnoreCase("Stella Cadente", "CADENTE")}

5.7.3 endsWith函数

endsWith函数用于测试一个字符串是否以指定的后缀结尾,其返回值是一个Boolean,语法如下:

1
endsWith(string, suffix)

例如,下列的EL表达式将返回true

1
${fn:endsWith("Hello World", "World")}

5.7.4 escapeXml函数

escapeXml函数用于给String编码。这种转换与out标签将其escapeXml属性设为true一样。escapeXml的语法如下:

1
escapeXml(string)

例如,下面的EL表达式:

1
${fn:escapeXml("Use<br/>to change lines")}

将被渲染成:

1
Use &lt;br/&gt; to change lines

这样浏览器就不会把<br/>当成换行符,而是显示<br/>这个字符串本身。

5.7.5 indexOf函数

indexOf函数返回指定子字符串在某个字符串中第一次出现时的索引。**如果没有找到指定的子字符串,则返回−1**。其语法如下:

1
indexOf(string, substring)

例如,下列的EL表达式将返回7:

1
${fn:indexOf("Stella Cadente", "Cadente")}

5.7.6 join函数

join函数将一个String数组中的所有元素都合并成一个字符串并用指定的分隔符分开,其语法如下:

1
join(array, separator)

如果这个数组为null,就会返回一个空字符串。

1
${fn:join(myArray,",")}

将返回“my, world”。

5.7.7 length函数

length函数用于返回集合中的项目数,或者字符串中的字符数,其语法如下:

1
length{input}

下列的EL表达式将返回14:

1
${fn:length("Stella Cadente", "Cadente")}

5.7.8 replace函数

replace函数将字符串中出现的所有beforeStringafterString替换,并返回结果,其语法如下:

1
replace(string, beforeSubstring, afterSubstring)

例如,下列的EL表达式将返回“StElla CadEntE”:

1
${fn:replace("Stella Cadente", "e", "E")}

5.7.9 split函数

split函数用于将一个字符串分离成一个子字符串数组。它的作用与join相反。例如,下列代码是分离字符串“my,world”,并将结果保存在有界变量split中。随后,利用forEach标签将split格式化成一个HTML表:

1
2
3
4
5
6
<c:set var="split" value='${fn:split("my,world",",")}'/>
<table>
<c:forEach var="substring" items="${split}">
<tr><td>${substring}</td></tr>
</c:forEach>
</table>

结果为:

1
2
3
4
<table>
<tr><td>my</td></tr>
<tr><td>world</td></tr>
</table>

5.7.10 startsWith函数

substring函数用于返回一个从指定的起始索引(含)到指定的终止索引的子字符串,其语法如下:

1
substring(string, beginIndex, endIndex)

下列的EL表达式将返回“Stel”:

1
${fn:substring("Stella Cadente", 0, 4)}

5.7.12 substringAfter函数

substringAfter函数用于返回指定子字符串第一次出现后的字符串部分,其语法如下:

1
substringAfter(string, substring)

例如,下列的EL表达式将返回“lla Cadente”:

1
${fn:substringAfter("Stella Cadente", "e")}

5.7.13 substringBefore函数

substringBefore函数用于返回指定子字符串第一次出现前的字符串部分,其语法如下:

1
substringBefore(string, substring)

例如,下列的EL表达式将返回“St”:

1
${fn:substringBefore("Stella Cadente", "e")}

5.7.14 toLowerCase函数

toLowerCase函数将一个字符串转换成它的小写版本,其语法如下:

1
toLowerCase(string)

例如,下列的EL表达式将返回“stella cadente”:

1
${fn:toLowerCase("Stella Cadente")}

5.7.15 toUpperCase函数

toUpperCase函数将一个字符串转换成它的大写版本,其语法如下:

1
toUpperCase(string)

例如,下列的EL表达式将返回“STELLA CADENTE”:

1
${fn:toUpperCase("Stella Cadente")}

5.7.16 trim函数

trim函数用于删除一个字符串开头和结尾的空白,其语法如下:

1
trim(string)

例如,下列的EL表达式将返回“Stella Cadente”:

1
${fn:trim("            Stella Cadente      ")}

5.6.6 parseDate标签

parseDate标签以区分地域的格式解析以字符串表示的日期和时间,其语法有两种形式。第一种形式没有body content

1
2
3
4
5
6
7
8
9
<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}"]/>

第二种形式有body content

1
2
3
4
5
6
7
8
9
10
<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>

body contentJSP

parseDate标签的属性

属性 类型 描述
value+ 字符串 要解析的字符串
type+ 字符串 说明要解析的字符串中是否包含日期、时间或二者均有
dateStyle+ 字符串 日期的格式化样式
timeStyle+ 字符串 时间的格式化样式
pattern+ 字符串 定制格式化样式,决定要如何解析该字符串
timeZone+ 字符串或者java.util.TimeZone 定义时区,使日期字符串中的时间信息均根据它来解析
parseLocale+ 字符串或者java.util.Locale 定义locale,在解析操作期间用其默认为格式化样式,或将pattern属性定义的样式应用其中
var 字符串 保存输出结果的有界变量名称
scope 字符串 var的范围

实例

下面的parseDate标签用于解析有界变量myDate引用的日期,并将得到的java.util.Date保存在一个页面范围的有界变量formattedDate中:

解析字符串为Date对象 并保存为页面有界变量

1
2
3
<c:set var="myDate" value="12/12/2005"/>
<fmt:parseDate var="formattedDate" type="date"
dateStyle="short" value="${myDate}"/>

使用保存的时间有界变量

1
<strong>${formattedDate}</strong>

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>parseDate Test</title>
</head>
<body>
<c:set var="myDate" value="2019-4-6" />
<fmt:parseDate var="formattedDate" type="date"
dateStyle="default"
value="${myDate}" />
<strong>${formattedDate}</strong>
</body>
</html>

运行结果:

1
Sat Apr 06 00:00:00 CST 2019

5.6.5 parseNumber标签

parseNumber标签用于将以字符串表示的数字、货币或者百分比解析成数字,其语法有两种形式。第一种形式没有body content

1
2
3
4
5
6
7
8
<fmt:parseNumber value="numericValue"
[type="{number|currency|percent}"]
[pattern="customPattern"]
[parseLocale="parseLocale"]
[integerOnly="{true|false}"]
[var="varName"]
[scope="{page|request|session|application}"]
/>

第二种形式有 body content

1
2
3
4
5
6
7
8
<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>

body contentJSP
下面的parseNumber标签用于解析有界变量quantity引用的值,并将结果保存在有界变量formattedNumber中:

1
2
<fmt:parseNumber var="formattedNumber" type="number"
value="${quantity}"/>

parseNumber标签的属性

属性 类型 描述
value+ 字符串或数字 要解析的字符串
type+ 字符串 说明该字符串是要被解析成数字、货币,还是百分比
pattern+ 字符串 定制格式化样式,决定value属性中的字符串要如何解析
parseLocale+ 字符串或者java.util.Locale 定义locale,在解析操作期间将其默认为格式化样式,或将pattern属性定义的样式应用其中
integerOnly+ 布尔 说明是否只解析指定值的整数部分
var 字符串 保存输出结果的有界变量名称
scope 字符串 var的范围