65.9K
CodeProject 正在变化。 阅读更多。
Home

JSP - JSTL 自定义标签库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (19投票s)

2008年12月9日

CPOL

7分钟阅读

viewsIcon

152639

JSP 自定义标签提供了一种标准化的机制,用于在动态网页中分离表示层和业务逻辑,从而使页面设计者能够专注于表示层,而应用程序开发人员则编写后端代码。

JSP 自定义标签

JSP 自定义标签提供了一种标准化的机制,用于在动态网页中分离表示层和业务逻辑,从而使页面设计者能够专注于表示层,而应用程序开发人员则编写后端代码。它们只是实现了特殊接口的 Java 类。一旦开发并部署了自定义标签,就可以使用 XML 语法从 HTML 调用其操作。标签有开始标签和结束标签。标签可以有主体内容,也可以没有。自定义标签可以表示为

<tagLibrary:tagName attribute="value">
       body
</tagLibrary:tagName>

自定义标签的优势

关于 JSP 自定义标签,一个非常重要的一点是,它们并不提供比脚本片段 (scriptlets) 更多的功能。它们只是通过帮助我们改进业务逻辑和表示逻辑的分离,从而提供更好的封装。自定义标签的一些优势包括:

  • 它可以减少或消除我们 JSP 应用程序中的脚本片段。传递给标签的任何必要参数都可以作为属性或作为标签体内容传递,因此不需要 Java 代码来初始化或设置组件属性。
  • 它具有更简单的语法。脚本片段用 Java 编写,而自定义标签可以用类似 HTML 的语法使用。
  • 通过允许非程序员内容开发者执行 HTML 无法完成的任务,可以提高他们的生产力。
  • 它是可重用的。它节省了开发和测试时间。脚本片段除非我们进行复制粘贴重用,否则是不可重用的。

实现 JSP 自定义标签

开发自定义标签涉及几个步骤。这些步骤可总结如下:

  1. 编写标签处理类。
  2. 创建标签库描述符 (TLD)。
  3. 使 TLD 文件和处理类可访问。
  4. 引用标签库。
  5. 在 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 应用程序可访问。为了使用该标签,我们必须引用它,这可以通过三种方式完成:

  1. 引用未打包标签库的标签库描述符。例如:
  2. <%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
  3. 引用包含标签库的 JAR 文件。例如:
  4. <%@ taglib uri="/WEB-INF/lib/customTag.jar" prefix="ct" %>
  5. 从 Web 应用程序描述符 (web.xml) 定义对标签库描述符的引用,并定义一个简短的名称以便在 JSP 中引用标签库。
  6. <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>

结果应类似于下图所示:

JSTL-TodayTag.png

参数化标签

要处理属性以开发参数化标签,需要提及两点:

  1. 在标签处理程序中添加一个 set 方法
  2. customTag.tld 添加一个新标签

我们可以参考我们之前示例中突出显示的部分。

带主体的标签

对于带主体的标签,标签处理程序的实现方式不同,取决于主体是需要评估一次还是多次。

  • 单次评估:如果主体需要评估一次,标签处理程序应实现 Tag 接口,或扩展 TagSupport 抽象类;doStartTag 方法需要返回 EVAL_BODY_INCLUDE,如果根本不需要评估,则应返回 BODY_SKIP
  • 多次评估:如果主体需要评估多次,则应实现 BodyTag 接口。BodyTag 接口扩展了 Tag 接口,并定义了额外的(setBodyContentdoInitBodydoAfterBody)方法,使标签处理程序能够检查并可能修改其主体。或者,类似于 TagSupport 类,我们可以扩展 BodyTagSupport 类,它为 BodyTag 接口中的方法提供了默认实现。通常,我们需要实现 doInitBodydoAfterBody 方法。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> 标签的值必须是 JSPjspcontent

现在,下面是此示例的测试驱动程序:

<%@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>

结果应类似于下图所示:

JSTL-UpperLowerCase.png

多次评估

现在让我们来看一个标签主体被评估多次的示例。示例标签接受一个整数作为其属性,并在 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>

输出应类似于下图所示:

JSTL-LoopText.png

结论

自定义标签有助于我们改进程序逻辑和表示的分离。本文中的各种示例展示了如何开发和部署简单和高级的自定义标签。此外,您可能还想看看 Jakarta Taglibs 项目 提供各种自定义标签库。有关更多信息,您可以参考以下网站:

© . All rights reserved.