JSP - JSTL 自定义标签库






4.93/5 (19投票s)
JSP 自定义标签提供了一种标准化的机制,用于在动态网页中分离表示层和业务逻辑,从而使页面设计者能够专注于表示层,而应用程序开发人员则编写后端代码。
JSP 自定义标签
JSP 自定义标签提供了一种标准化的机制,用于在动态网页中分离表示层和业务逻辑,从而使页面设计者能够专注于表示层,而应用程序开发人员则编写后端代码。它们只是实现了特殊接口的 Java 类。一旦开发并部署了自定义标签,就可以使用 XML 语法从 HTML 调用其操作。标签有开始标签和结束标签。标签可以有主体内容,也可以没有。自定义标签可以表示为
<tagLibrary:tagName attribute="value">
body
</tagLibrary:tagName>
自定义标签的优势
关于 JSP 自定义标签,一个非常重要的一点是,它们并不提供比脚本片段 (scriptlets) 更多的功能。它们只是通过帮助我们改进业务逻辑和表示逻辑的分离,从而提供更好的封装。自定义标签的一些优势包括:
- 它可以减少或消除我们 JSP 应用程序中的脚本片段。传递给标签的任何必要参数都可以作为属性或作为标签体内容传递,因此不需要 Java 代码来初始化或设置组件属性。
- 它具有更简单的语法。脚本片段用 Java 编写,而自定义标签可以用类似 HTML 的语法使用。
- 通过允许非程序员内容开发者执行 HTML 无法完成的任务,可以提高他们的生产力。
- 它是可重用的。它节省了开发和测试时间。脚本片段除非我们进行复制粘贴重用,否则是不可重用的。
实现 JSP 自定义标签
开发自定义标签涉及几个步骤。这些步骤可总结如下:
- 编写标签处理类。
- 创建标签库描述符 (TLD)。
- 使 TLD 文件和处理类可访问。
- 引用标签库。
- 在 JSP 页面中使用该标签。
第一步:编写标签处理类
我们需要做的第一件事是编写标签处理类。标签处理程序 (tag handler) 是一个由 JSP 运行时调用的对象,用于在执行引用该标签的 JSP 页面期间评估自定义标签。在标签评估的各个点, JSP 容器会调用标签处理程序的类方法。所有标签都必须实现 Tag
接口。在我们的示例 TodayTag
中,我们不需要包含标签体内容,因为它只以用户定义的格式显示当前日期。因此,我们的处理程序类将实现 Tag
接口(通常通过扩展 TagSupport
类)。如果我们要创建一个能够处理标签体内容的标签,我们将需要实现 BodyTag
接口(通常通过扩展 BodyTagSupport
类)。
package jstl;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
/**
* @author Bikash Shaw
*/
public class TodayTag extends TagSupport{
private String mFormat;
public void setFormat(String pFormat) {
mFormat = pFormat;
}
public int doStartTag() throws JspException {
try {
JspWriter out = pageContext.getOut();
Date today = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat(mFormat);
out.print(dateFormatter.format(today));
} catch(IOException ioe) {
throw new JspException("Error: " + ioe.getMessage());
}
return SKIP_BODY;
}
public int doEndTag() throws JspException {
return SKIP_PAGE;
}
}
第二步:创建标签库描述符 (TLD)
下一步是定义将包含我们的自定义标签与将处理它的 Java 类(或类)之间的映射的库。此库定义在一个名为标签库描述符 (TLD) 的 XML 文档中。我们将调用自定义标签示例的 TLD 为 customTag.tld。
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>ct</short-name>
<uri>/WEB-INF/customTag</uri>
<tag>
<name>today</name>
<tag-class>jstl.TodayTag</tag-class>
<body-content>empty</body-content>
<info>This Tag Displayes current server date in usser defined format</info>
<attribute>
<name>format</name>
<required>true</required>
<description>Provide a display format</description>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
所有关键信息都包含在 Tag
标签中,其中映射了标签名称和处理程序类。在更复杂的情况下,我们可以使用额外的 XML 标签来提供有关库和标签的更多信息。在一个库中定义多个标签也是很常见的。
第三步:使 TLD 文件和处理类可访问
第三步是使类或类和 TLD 对 Web 应用程序可访问。有两种方法可以做到这一点。我们可以将类和 TLD 打包到一个 JAR 文件中,然后将 JAR 文件存储在 Web 应用程序的 lib 目录中,或者我们可以将类文件松散地放在 classes 子目录中,并将 TLD 文件放在 Web 应用程序的 WEB-INF 目录下的某个位置。
第四步:引用标签库
第四步是使类或类和 TLD 对 Web 应用程序可访问。为了使用该标签,我们必须引用它,这可以通过三种方式完成:
- 引用未打包标签库的标签库描述符。例如:
- 引用包含标签库的 JAR 文件。例如:
- 从 Web 应用程序描述符 (web.xml) 定义对标签库描述符的引用,并定义一个简短的名称以便在 JSP 中引用标签库。
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<%@ taglib uri="/WEB-INF/lib/customTag.jar" prefix="ct" %>
<taglib>
<taglib-uri>customTag</taglib-uri>
<taglib-location>/WEB-INF/customTag.tld</taglib-location>
</taglib>
接下来,我们向需要使用自定义标签库的任何页面添加一个 JSP 声明:
<%@ taglib uri="customTag" prefix="ct" %>
第五步:在 JSP 页面中使用该标签
现在,我们就可以在 JSP 页面中使用我们的自定义标签了。
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSTL Custom Tag Today</title>
</head>
<body>
<h2 align="center"><ct:today format="MMMM dd, yyyy"/></h2>
</body>
</html>
结果应类似于下图所示:
参数化标签
要处理属性以开发参数化标签,需要提及两点:
- 在标签处理程序中添加一个 set 方法
- 向 customTag.tld 添加一个新标签
我们可以参考我们之前示例中突出显示的部分。
带主体的标签
对于带主体的标签,标签处理程序的实现方式不同,取决于主体是需要评估一次还是多次。
- 单次评估:如果主体需要评估一次,标签处理程序应实现
Tag
接口,或扩展TagSupport
抽象类;doStartTag
方法需要返回EVAL_BODY_INCLUDE
,如果根本不需要评估,则应返回BODY_SKIP
。 - 多次评估:如果主体需要评估多次,则应实现
BodyTag
接口。BodyTag
接口扩展了Tag
接口,并定义了额外的(setBodyContent
、doInitBody
和doAfterBody
)方法,使标签处理程序能够检查并可能修改其主体。或者,类似于TagSupport
类,我们可以扩展BodyTagSupport
类,它为BodyTag
接口中的方法提供了默认实现。通常,我们需要实现doInitBody
和doAfterBody
方法。doInitBody
在主体内容设置后但在评估前调用,而doAfterBody
在主体内容评估后调用。
单次评估
这是一个单次评估的示例,我们扩展了 BodyTagSupport
类。此示例读取主体内容,并根据我们提供的属性将其转换为大写或小写。
首先,我们需要创建标签处理类:
package jstl;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
*
* @author Bikash Shaw
*/
public class ChangeCaseTag extends BodyTagSupport {
private String mCase;
public void setCase(String pCase) {
mCase = pCase;
}
public int doAfterBody() throws JspException {
try {
BodyContent bc = getBodyContent();
String body = bc.getString();
JspWriter out = bc.getEnclosingWriter();
if (body != null) {
if ("upper".equalsIgnoreCase(mCase)) {
out.print(body.toUpperCase());
} else {
out.print(body.toLowerCase());
}
}
} catch (IOException ioe) {
throw new JspException("Error: " + ioe.getMessage());
}
return SKIP_BODY;
}
}
下一步是在标签库描述符文件 customTag.tld 中添加一个标签:
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>ct</short-name>
<uri>/WEB-INF/customTag</uri>
<tag>
<name>changeCase</name>
<tag-class>jstl.ChangeCaseTag</tag-class>
<body-content>JSP</body-content>
<info>This Tag Displayes current server date in user defined format</info>
<attribute>
<name>case</name>
<required>true</required>
<description>Provide the case upper or lower</description>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
请注意,当您编写带主体的标签时,<body-content>
标签的值必须是 JSP 或 jspcontent。
现在,下面是此示例的测试驱动程序:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Upper or Lower Case</title>
</head>
<body>
<h2>
Upper Case :
<ct:changeCase case="upper">
Hello World
</ct:changeCase>
</h2>
<h2>
Lower Case :
<ct:changeCase case="lower">
Hello World
</ct:changeCase>
</h2>
</body>
</html>
结果应类似于下图所示:
多次评估
现在让我们来看一个标签主体被评估多次的示例。示例标签接受一个整数作为其属性,并在 JSP 代码中指示的次数打印其主体 HTML。
再次,首先创建一个标签处理类:
package jstl;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
*
* @author Bikash Shaw
*/
public class LoopTextTag extends BodyTagSupport {
private int mTimes = 0;
private BodyContent mBodyContent;
public void setTimes(int pTimes) {
mTimes = pTimes;
}
public void setBodyContent(BodyContent pBodyContent) {
mBodyContent = pBodyContent;
}
public int doStartTag() throws JspException {
if (mTimes > 1) {
return EVAL_BODY_TAG;
} else {
return SKIP_BODY;
}
}
public int doAfterBody() throws JspException {
if (mTimes > 1) {
mTimes--;
return EVAL_BODY_TAG;
} else {
return SKIP_BODY;
}
}
public int doEndTag() throws JspException {
try {
if (mBodyContent != null) {
mBodyContent.writeOut(mBodyContent.getEnclosingWriter());
}
} catch (IOException pIOEx) {
throw new JspException("Error: " + pIOEx.getMessage());
}
return EVAL_PAGE;
}
}
此标签的方法执行以下角色:
doStartTag
方法在标签开始时被调用。它检查是否需要执行循环。- JSP 容器调用
setBodyContent
来检查是否需要多个循环。 doAfterBody
方法在每次评估后调用;循环需要执行的次数减一,然后当次数不大于一时,它返回SKIP_BODY
。doEndTag
方法在标签结束时被调用,并将内容(如果有)写入封闭的 writer。
与前面的示例类似,下一步是向 customTag.tld 添加一个新的标签描述符:
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>ct</short-name>
<uri>/WEB-INF/customTag</uri>
<tag>
<name>loopText</name>
<tag-class>jstl.LoopTextTag</tag-class>
<body-content>JSP</body-content>
<info>This Tag Displayes given text multiple times</info>
<attribute>
<name>times</name>
<required>true</required>
<description>Provide number of times the text will be repeated</description>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
请注意,<rtexprvalue>
标签为 true
,这表示将在运行时进行评估。
最后,我们编写一个样本 JSP 页面来测试 <loopText>
标签。
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>JSTL Custom Tag Loop Text</title>
</head>
<body>
<center>
<ct:loopText times="5">
<h3>Loop Text!</h3>
</ct:loopText>
</center>
</body>
</html>
输出应类似于下图所示:
结论
自定义标签有助于我们改进程序逻辑和表示的分离。本文中的各种示例展示了如何开发和部署简单和高级的自定义标签。此外,您可能还想看看 Jakarta Taglibs 项目 提供各种自定义标签库。有关更多信息,您可以参考以下网站: