加载头像

第三章 Tag 文件与 Tag 标记

3.1 Tag 文件

Tag 文件的结构

Tag 文件是扩展名为 .tag 的文本文件,其结构和 JSP 文件类似。

一个 Tag 文件中可以包含:

  • 普通的 HTML 标记符
  • 某些特殊的指令标记
  • 成员变量声明和方法的定义
  • Java 程序片和 Java 表达式

以下是一个简单的 Tag 文件 oddNumberSum.tag,负责计算 100 内的全部奇数的代数和。

oddNumberSum.tag

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ tag pageEncoding="utf-8" %>
<p style="font-family: 宋体;font-size: 36">
1~100 内的奇数之和:
<%
int sum = 0,i = 1;
for(i = 1; i <= 100; i++){
if(i % 2 == 1){
sum = sum + i;
}
}
out.println(sum);
%>
</p>

Tag 文件的保存

Tag 文件所在目录

Tag 文件可以实现代码的复用,即 Tag 文件可以被许多 JSP 页面使用。为了能让一个 Web 应用中的 JSP 页面使用某一个 Tag 文件,必须把这个 Tag 文件存放到 Tomcat 服务器指定的目录中。

也就是说,如果某个 Web 服务目录下的 JSP 页面准备调用一个 Tag 文件,那么必须在该 Web 服务目录下,建立如下的目录结构:

1
Web 服务目录\WEB-INF\tags\

其中的 WEB-INF 和 tags 都是固定的目录名称,而 tags 下的子目录名称可以任意给定。

一个 Tag 文件必须保存到 tags 目录或其下的子目录中。

Tag 文件的编码

保存 Tag 文件时按照 Tag 文件指定的编码保存,例如 Tag 文件使用 tag 指令:

1
<%@ tag pageEncoding="utf-8" %>

3.2 Tag 标记

Tag 标记与 Tag 文件

某个 Web 服务目录下的 Tag 文件只能由该 Web 服务目录中的 JSP 页面调用,JSP 页面必须通过 Tag 标记来调用一个 Tag 文件。

Tag 标记的名字和 Tag 文件的名字一致,也就是说,当我们编写了一个 Tag 文件并保存到特定目录中后,也就给出了一个 Tag 标记,该标记的格式为:

1
<Tag文件的名字/>

或者

1
<Tag文件的名字>其他内容(称为标体内容)</Tag文件的名字>

一个 Tag 文件对应着一个 Tag 标记,把全体 Tag 标记称之为一个自定义标记库或简称为标记库

Tag 标记的使用

一个 JSP 页面通过使用 Tag 标记来调用一个 Tag 文件。Web 服务目录下的一个 JSP 页面在使用 Tag 标记来调用一个 Tag 文件之前,必须首先使用 taglib 指令标记引入该 Web 服务目录下的标记库,只有这样,JSP 页面才可以使用 Tag 标记调用相应的 Tag 文件。

taglib 指令的格式如下:

1
<%@ taglib tagdir="标记库的位置" prefix="前缀" %>

例如:

1
<%@ taglib tagdir="/WEB-INF/tags" prefix="computer" %>

引入标记库后,JSP 页面就可以使用带前缀的 Tag 标记调用相应的 Tag 文件,其中的前缀由 <taglib> 指令中的 prefix 属性指定。例如 JSP 如下使用 Tag 标记调用相应的 Tag 文件:

1
<computer:oddNumberSum/>

taglib 指令中的 prefix 给出的前缀由用户自定义,其好处是,通过前缀可以有效地区分不同标记库中具有相同名字的标记文件。

以下 JSP 页面使用了 Tag 标记调用 oddNumberSum.tag 文件,用来计算 1~100 之内的奇数之和:

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html" %>
<%@ page pageEncoding="utf-8" %>
<%@ taglib tagdir="/WEB-INF/tags" prefix="computer" %>
<html>
<body background=cyan>
<h1>调用 Tag 文件计算 1~100 内的奇数之和</h1>
<computer:oddNumberSum/><%-- 使用 Tag 标记 --%>
</body>
</html>

Tag 标记的运行原理

Tomcat 服务器处理 JSP 页面中的 Tag 标记的原理如下:

  • 如果该 Tag 标记对应的 Tag 文件是首次被 JSP 页面调用,那么 Tomcat 服务器会将 Tag 文件转译成一个 Java 文件,并编译这个 Java 文件生成字节码文件,然后执行这个字节码文件(这和执行 JSP 页面的原理类似)。
  • 如果该 Tag 文件已经被转编译为字节码文件,Tomcat 服务器将直接执行这个字节码文件。
  • 如果对 Tag 文件进行了修改,那么 Tomcat 服务器会重新将 Tag 文件转译成一个 Java 文件,并编译这个 Java 文件生成字节码文件,然后执行这个字节码文件。

3.3 Tag 文件中的常用指令

与 JSP 文件类似,Tag 文件中也有一些常用指令,这些指令将影响 Tag 文件的行为。Tag 文件中经常使用的指令有 tagtaglibincludeattributevariable

tag 指令

Tag 文件中的 tag 指令类似于 JSP 文件中的 page 指令。Tag 文件通过 tag 指令可以指定某些属性的值,以便从总体上影响 Tag 文件的处理和表示。

tag 指令的语法如下:

1
<%@ tag 属性1="属性1的值" 属性2="属性2的值" ... 属性n="属性n的值" %>

或者

1
2
3
4
<%@ tag 属性1="属性1的值" %>
<%@ tag 属性2="属性2的值" %>
...
<%@ tag 属性n="属性n的值" %>

pageEncoding 属性

在 Tag 文件中,tag 指令的 pageEncoding 属性的默认值是 ISO-8859-1。

因此,为了避免显示信息出现乱码现象,Tag 文件必须将该属性值设置为 UTF-8。

1
<%@ tag pageEncoding="utf-8" %>

language 属性

language 属性定义 Tag 页面使用的脚本语言,该属性的值目前只能取 java。其默认值就是 java,因此没有必要在编写 Tag 文件时特意指定。

language 属性指定值的格式是:

1
<%@ tag language="java" %>

import 属性

该属性的作用是为Tag 页面引入 Java 运行环境提供的包中的类,这样就可以在 Tag 页面的程序片部分、变量及方法定义部分以及表达式部分使用包中的类。可以为该属性指定多个值,该属性的值可以是某包中的所有类或一个具体的类,例如:

1
<%@ tag import="java.io.*","java.time.LocalDate" %>

这样 JSP 引擎把 Tag 页面转译成的 Java 文件中会有如下的 import 语句:

1
2
import java.io.*;
import java.time.LocalDate;

import 属性默认已经有如下值:

1
2
3
4
<%@ tag import="java.lang.*" %>
<%@ tag import="javax.servlet.*" %>
<%@ tag import="javax.servlet.jsp.*" %>
<%@ tag import="javax.servlet.http.*" %>

include 指令

在 Tag 文件中也有和 JSP 文件类似的 include 指令标记,其使用方法和作用与 JSP 文件中的 include 指令标记类似。

attribute 指令

在 Tag 文件中通过使用 attribute 指令让使用它的 JSP 页面向该 Tag 文件传递需要的数据。

attribute 指令的格式如下:

1
<%@ attribute name="对象名字" required="true"/"false" type="对象的类型" %>

例如:

1
<%@ attribute name="result" required="true" type="java.lang.Double" %>

此时该 Tag 文件需要等待调用它的 JSP 页面将一个 Double 类型的对象的引用传递给 result。

JSP 页面需要使用以下格式来传递引用:

1
<前缀:Tag文件名字 对象名字="对象的引用">

variable 指令

Tag 文件通过使用 attribute 指令,可以使得调用该 Tag 文件的 JSP 页面动态地向其传递数据。在某些 Web 应用中,JSP 页面不仅希望向 Tag 文件传递数据,而且希望 Tag 文件能返回数据给 JSP 页面。比如,许多 JSP 页面可能都需要调用某个 Tag 文件对某些数据进行基本的处理,但不希望 Tag 文件做进一步的特殊处理以及显示数据,因为各个 JSP 页面对数据的进一步处理或显示格式的要求是不同的。因此,JSP 页面希望 Tag 文件将数据的基本处理结果存放在某些对象中,将这些对象返回给当前 JSP 页面即可。

Tag 文件通过使用 variable 指令可以将 Tag 文件中的对象返回给调用该 Tag 文件的 JSP 页面。

variable 指令的格式如下:

1
<%@ variable name-given="对象名" variable-class="对象类型" scope="有效范围" %>
  • name-given 的值就是 Tag 文件返回给 JSP 页面的对象。该对象的名字必须符合标识符规定,即名字可以由字母、下划线、美元符号和数字组成,并且第一个字符不能是数字字符。
  • variable-class 的值是返回的对象的类型,对象的类型必须带有包名,比如 java.lang.Double、java.time.LocalDate 等类型。如果 variable 指令中没有使用 ariable-class 给出对象的类型,那么对象的类型是 java.lang.String 类型。
  • scope 属性的值指定对象的有效范围 scope 的值可以取 AT_BEGIN、NESTED 和 AT_END。
    • scope 的值是 AT_BEGIN 时,JSP 页面一旦开始使用 Tag 标记,就得到了 variable 指令返回给 JSP 页面的对象,JSP 页面就可以在 Tag 标记的标记体中或 Tag 标记结束后的各个部分中使用 variable 指令返回给 JSP 页面的对象。
    • scope 的值是 NESTED 时,JSP 页面只可以在 Tag 标记的标记体中使用 variable 指令返回给 JSP 页面的对象。
    • scope 的值是 AT_END 时,JSP 页面只可以在 Tag 标记结束后。才可以使用 variable 指令返回给 JSP 页面的对象。

下面的 variable 指令给出的对象的名字是 time,类型为 java.time.LocalDate,有效范围是 AT_END:

1
<%@ variable name-given="time" variable-class="java.time.LocalDate" scope="AT_END" %>

对象的返回

Tag 文件为了给 JSP 页面返回一个对象,就必须将返回的对象的名字以及该对象的引用存储到 Tomcat 服务器提供的内置对象 jspContext 中。Tag 文件只有将对象的名字及其引用存储到 jspContext 中, JSP 页面才可以使用该对象。

比如,Tag 文件的 variable 指令:

1
<%@ variable name-given="time" variable-class="java.time.LocalDate" scope="AT_END" %>

为 JSP 页面返回名字是 time 的 LocalDate 对象。那么 Tag 文件中必须让 jspContext 调用 setAttribute() 方法存储名字是 time 的对象以及该对象的引用。

例如:

1
jspContext.setAttribute("time",LocalDate.now());

将名字是 time 的 LocalDate 对象存储到 jspContext 中。

taglib 指令

JSP 页面和 Tag 页面都可以使用 taglib 指令引入标记库:

1
<%@ taglib tagdir="标记库的位置" prefix="前缀" %>

可以引入多个标记库:

1
2
3
4
<%@ taglib tagdir="标记库1的位置" prefix="前缀1" %>
<%@ taglib tagdir="标记库2的位置" prefix="前缀2" %>
...
<%@ taglib tagdir="标记库n的位置" prefix="前缀n" %>

习题 3

  1. 用户可以使用浏览器直接访问一个 Tag 文件吗?

    答:Tag 文件和 JSP 文件很类似,可以被 JSP 文件动态加载调用,但是用户不能直接访问 Tag 文件。

  2. Tag 文件应当存放在怎样的目录中?

    答:Tag 文件的保存位置为:WEB-INF 目录下的 tags 目录及其子目录中。

  3. Tag 文件中的 tag 指令可以设置哪些属性的值?

    答:tag 指令可以设置 pageEncodinglanguageimport 等属性的值。

  4. Tag 文件中的 attribute 指令有怎样的作用?

    答:在 Tag 文件中通过使用 attribute 指令让使用它的 JSP 页面向该 Tag 文件传递需要的数据。

  5. Tag 文件中的 variable 指令有怎样的作用?

    答:Tag 文件通过使用 variable 指令可以将 Tag 文件中的对象返回给调用该 Tag 文件的 JSP 页面。

  6. 编写两个 Tag 文件 Rect.tagCircle.tagRect.tag 负责计算并显示矩形的面积,Circle.tag 负责计算并显示圆的面积。编写一个 JSP 页面 lianxi6.jsp,该 JSP 页面使用 Tag 标记调用 Rect.tagCircle.tag。调用 Rect.tag 时,向其传递矩形的两个边的长度;调用 Circle.tag 时,向其传递圆的半径。

    答:代码如下:

    Rect.tag

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <%@ tag pageEncoding="utf-8" %>
    <%@ attribute name="sideA" required="true" %>
    <%@ attribute name="sideB" required="true" %>
    <%@ variable name-given="area1" variable-class="java.lang.Double" scope="AT_END" %>
    <%!
    public double getArea(double a, double b) {
    if (a > 0 && b > 0) {
    double area = a * b;
    return area;
    } else {
    return -1;
    }
    }
    %>
    <%
    try {
    double a = Double.parseDouble(sideA);
    double b = Double.parseDouble(sideB);
    double result = getArea(a, b);
    jspContext.setAttribute("area1", new Double(result));
    } catch (Exception e) {
    jspContext.setAttribute("area1", new Double(-1.0));
    }
    %>

    Circle.tag

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <%@ tag pageEncoding="utf-8" %>
    <%@ attribute name="radius" required="true" %>
    <%@ variable name-given="area2" variable-class="java.lang.Double" scope="AT_END" %>
    <%!
    public double getArea(double r) {
    if (r > 0) {
    double area = Math.PI * r * r;
    return area;
    } else {
    return -1;
    }
    }
    %>
    <%
    try {
    double r = Double.parseDouble(radius);
    double result1 = getArea(r);
    jspContext.setAttribute("area2", new Double(result1));
    } catch (Exception e) {
    jspContext.setAttribute("area2", new Double(-1.0));
    }
    %>

    Lianxi6.jsp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    <%@ page contentType="text/html;charset=UTF-8"%>
    <%@ taglib tagdir="/WEB-INF/tags/test_3_6" prefix="computer" %>
    <html>
    <body>
    <form action="" method=get name=form>
    <h3>请输入矩形的长宽和圆的半径:</h3>
    <table>
    <tr>
    <td>长:</td>
    <td><input type="text" name="a"></td>
    </tr>
    <tr>
    <td>宽:</td>
    <td><input type="text" name="b"></td>
    </tr>
    <tr>
    <td>半径:</td>
    <td><input type="text" name="r"></td>
    </tr>
    </table>
    <br><input type="submit" value="计算" name=submit>
    </form>
    <%
    String a = request.getParameter("a");
    String b = request.getParameter("b");
    String r = request.getParameter("r");
    if (a == null || b == null || r == null) {
    a = "0";
    b = "0";
    r = "0";
    }
    if (a.length() > 0 && b.length() > 0 && r.length() > 0) {
    %>
    <computer:Rect sideA="<%=a%>" sideB="<%=b%>" />
    <computer:Circle radius="<%=r%>" />
    <br>矩形面积:
    <br><%=area1%>
    <br>圆形面积:
    <br><%=area2%>
    <%
    }
    %>
    </body>
    </html>

    运行截图:

    3-1 计算矩形和圆的面积

  7. 编写一个 Tag 文件:GetArea.tag 负责求出三角形的面积,并使用 variable 指令返回三角形的面积给调用该 Tag 文件的 JSP 页面。 JSP 页面负责显示 Tag 文件返回的三角形的面积。 JSP 在调用 Tag 文件时,使用 attribute 指令将三角形三边的长度传递给 Tag 文件。one.jsptwo.jsp 都使用 Tag 标记调用 GetArea.tagone.jsp 返回的三角形的面积保留最多 3 位小数,two.jsp 返回的三角形的面积保留最多 6 位小数。

    答:代码如下:

    one.jsp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    <%@ page contentType="text/html;charset=UTF-8" %>
    <%@ page import="java.text.*" %>
    <%@ taglib tagdir="/WEB-INF/tags/test_3_7" prefix="computer" %>
    <html>
    <body>
    <form action="" method=get name=form>
    <h3>请输入三角形三边长:</h3>
    <table>
    <tr>
    <td>a:</td>
    <td><input type="text" name="a"></td>
    </tr>
    <tr>
    <td>b:</td>
    <td><input type="text" name="b"></td>
    </tr>
    <tr>
    <td>c:</td>
    <td><input type="text" name="c"></td>
    </tr>
    </table>
    <br><input type="submit" value="计算结果最多保留 3 位小数" name=submit>
    <%
    String a=request.getParameter("a");
    String b=request.getParameter("b");
    String c=request.getParameter("c");
    if (a == null || b == null || c == null) {
    a = "0";
    b = "0";
    c = "0";
    }
    if (a.length() > 0 && b.length() > 0 && c.length()>0) {
    %>
    <computer:GetArea sideA="<%=a%>" sideB="<%=b%>" sideC="<%=c%>" />
    <%
    NumberFormat f=NumberFormat.getInstance();
    f.setMaximumFractionDigits(3);
    double result=area.doubleValue();
    String str=f.format(result);
    out.println(str);
    }
    %>
    </form>
    </body>
    </html>

    two.jsp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    <%@ page contentType="text/html;charset=UTF-8" %>
    <%@ page import="java.text.*" %>
    <%@ taglib tagdir="/WEB-INF/tags/test_3_7" prefix="computer" %>
    <html>
    <body>
    <form action="" method=get name=form>
    <h3>请输入三角形三边长:</h3>
    <table>
    <tr>
    <td>a:</td>
    <td><input type="text" name="a"></td>
    </tr>
    <tr>
    <td>b:</td>
    <td><input type="text" name="b"></td>
    </tr>
    <tr>
    <td>c:</td>
    <td><input type="text" name="c"></td>
    </tr>
    </table>
    <br> <input type="submit" value="计算结果最多保留 6 位小数" name=submit>
    <%
    String a = request.getParameter("a");
    String b = request.getParameter("b");
    String c = request.getParameter("c");
    if (a == null || b == null || c == null) {
    a = "0";
    b = "0";
    c = "0";
    }
    if (a.length() > 0 && b.length() > 0 && c.length() > 0) {
    %>
    <computer:GetArea sideA="<%=a%>" sideB="<%=b%>" sideC="<%=c%>" />
    <%
    NumberFormat f = NumberFormat.getInstance();
    f.setMaximumFractionDigits(6);
    double result = area.doubleValue();
    String str = f.format(result);
    out.println(str);
    }
    %>
    </form>
    </body>
    </html>

    GetArea.tag

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <%@ tag pageEncoding="utf-8" %>
    <%@ attribute name="sideA" required="true" %>
    <%@ attribute name="sideB" required="true" %>
    <%@ attribute name="sideC" required="true" %>
    <%@ variable name-given="area" variable-class="java.lang.Double" scope="AT_END" %>
    <%
    double a = Double.parseDouble(sideA);
    double b = Double.parseDouble(sideB);
    double c = Double.parseDouble(sideC);
    if (a + b > c && a + c > b && c + b > a) {
    double p = (a + b + c) / 2.0;
    double area = Math.sqrt(p * (p - a) * (p - b) * (p - c));
    jspContext.setAttribute("area",new Double(area));
    } else {
    jspContext.setAttribute("area",new Double(-1));
    }
    %>

    运行截图:

    3-2 计算三角形面积


返回目录 上一章 下一章
博客快捷键
shift K
关闭快捷键功能
shift A
打开/关闭中控台
shift M
播放/暂停音乐
shift D
深色/浅色显示模式
shift S
站内搜索
shift R
随机访问
shift H
返回首页
shift L
友链页面
shift P
关于本站
shift I
原版/本站右键菜单
引用到评论
随便逛逛博客分类文章标签
复制地址关闭热评深色模式轉為繁體