实现 JSP 自定义标记
JSP 体系结构需要以下组件以实现自定义标记:
- 在每一页中有一个 JSP 声明
- Web 应用程序描述符(web.xml)中的一个项
- 一个包含特殊 XML 文件和为处理自定义标记而调用的 Java 类的 JAR 文件
在下面几节,您将一步一步地学习如何满足这些要求并将自定义标记加入到 JSP 页面中。要想成功实现 JSP 自定义标记,您需要采取下面五个步骤:
- 编写标记处理程序类。
- 创建标记库描述符(TLD)。
- 使 TLD 文件和处理程序类可访问。
- 引用标记库。
- 在 JSP 页面中使用标记。
这些内容是相当基本的,也不用花很长时间。就让我们开始吧。
在下面的例子中,我们将使用一个非常简单的显示当前时间和日期的自定义标记例子。下面就是 DateTag
:
<abc:displayDate /> |
我们要做的第一件事是编写标记处理程序类。在执行引用自定义标记的 JSP 页面时,JSP 容器判断每一个自定义标记。当容器遇到一个标记时,它调用与这个自定义标记相关联的标记处理程序,我们将在后面更多地讨论这个过程。然后,每一个标记处理程序实现 JSP API 中的一个特殊接口。标记有两种类型:可以处理标记内容(或者正文)的标记和不能处理标记内容的标记:
<abc:tagWithNoBody attribute="value"/><abc:tagWithBody attribute="value">This is some body content that the tag handler can operate upon.</abc:tagWithBody> |
在 DateTag
例子中不需要加入正文内容,因为它只显示当前日期。因此,我们的处理程序类将实现 Tag
接口(一般是通过扩展 TagSupport
类)。如果我们要创建一个可以处理正文的标记,那么我们就需要实现 BodyTag
接口(一般是通过扩展 BodyTagSupport
类)。清单 1 显示了 DateTag
的处理程序类:
package myTags;import javax.servlet.jsp.tagext.*;import javax.servlet.jsp.*;import javax.servlet.http.*;import java.text.*;import java.util.*;public DateTag extends TagSupport { public int doStartTag() throws javax.servlet.jsp.JspException {HttpServletRequest req; Locale locale; HttpJspPage g; DateFormatdf; Stringdate; JSPWriterout; req = ( HttpServletRequest )pageContext.getRequest(); locale = req.getLocale(); df = SimpleDateFormat.getDateInstance( SimpleDateFormat.FULL,locale ); date = df.format( new java.util.Date() ); try { out = pageContext.getOut(); out.print( date ); } catch( IOException ioe ) { throw new JspException( "I/O Error : " + ioe.getMessage() ); }//end try/catch return Tag.SKIP_BODY; }//end doStartTag()}//end DateTag |
关于 DateTag
处理程序类有几件事值得一说。首先看一下方法声明。如果我们直接实现 Tag
接口,那么就需要完成几个方法声明。因为 TagSupport
类是简单的、具体类,它完全实现了在 Tag
接口中声明的方法,我们可以只实现那些在自定义标记中要使用的方法。在这个简单的例子中,我们只实现了 doStartTag()
方法,它是在遇到开始标记时调用的。
您还可能注意到 doStartTag()
方法返回 SKIP_BODY
。当然其原因是我们的简单日期标记没有正文。您要注意的最后一件重要的事情是使用了 pageContext
对象访问输出缓存以直接向输出流发送内容。您可能还记得在本系列以前的文章说过, pageContext
对象是一个隐式对象,它提供对与当前页面有关的属性的访问。
编写了源代码文件后,我们像编译所有其他 Java 类一样编译这个类(确保在类路径中加入 Servlet/JSP JAR 文件),然后将编译的类文件放到 Web 应用程序的类目录(WEB-INF/classes)中。如果我们开发的是几个标记或者定义有标记变量的标记,那么我们就会有多个标记处理程序类。在这种情况下我们可能选择将处理程序类打包在一个 JAR 文件中而不是使它们成为类目录中的分散的文件。
下一步是定义包含自定义标记与处理它的 Java 类(或多个类)之间的映射的库。这个库是在一个名为标记库描述符(TLD)的 XML 文档中定义的。我们将从 DateTag
例子 DateTagLib.tld中调用这个 TLD。注意“.tld”是这种文件的标准扩展名。
<?xml version="1.0" encoding="ISO-8859-1" ?><taglib> <tlibversion>1.0</tlibversion> <info>A simple tag library</info> <tag> <name>displayDate</name> <tagclass>myTags.DateTag</tagclass> <bodycontent>empty</bodycontent> <info>Display Date</info> </tag> </taglib> |
DateTagLib.tld 是一个出色的、最小的标记库描述符文件。所有关键信息都包含在 Tag
标记中,在这里映射了标记名和处理程序类,我们声明了标记对于正文内容的敏感性。对于更复杂的情况,我们可以使用其他的 XML 标记以提供有关库和标记的更多信息。在一个库中定义多个标记也很常见。
第 3 步是使这个类或者这些类和 TLD 可以被 Web 应用程序访问。有两种方法:可以将类和 TLD 打包到一个 JAR 文件中,再将这个 JAR 文件储存在 Web 应用程序的 lib 目录中,也可以将类文件分散地放到 classes 子目录中并将 TLD 文件放到 Web 应用程序的 WEB-INF 目录下面的某一位置。
在这个例子中,我们将使用第二种方法,将 TLD 文件和类分散地放到 Web 应用程序目录结构中。您可以回忆起在第 1 步中我们已经将标记处理程序类放到了 classes 目录中,所以我们实际上只需储存 TLD 文件。TLD 文件被储存在 WEB-INF 目录或者子目录中,如果是部署 Java 文件,则储存在 JAR 的 META-INF/ 目录或者子目录。在这里,我们没有使用 JAR 文件,所以我们只将 TLD 储存到 Web 应用程序的 WEB-INF/lib 目录中。
这时,我们已经编译了标记处理程序类、创建了 TLD 文件以定义处理程序类和标记之间的映射、并保证类和标记在应用程序中都是可访问的。下一步是建立 JSP 页面与标记库之间的引用。有两种方法声明 JSP 页面与其库之间的引用。可以通过 Web 应用程序描述符(web.xml)声明一个静态引用,也可以直接在页面中声明一个动态引用。我们将试用这两种方法。
为了进行静态引用,首先必须将下面的项加入到 web.xml 文件中:
<?xml version="1.0" encoding="ISO-8859-1" ?><Web-app> <!-- Define Servlets, Servlet Mappings, etc. --> <taglib> <taglib-uri>myTags</taglib-uri> <taglib-location>/WEB-INF/lib/DateTagLib.tld</taglib-location> </taglib> </Web-app> |
然后,将 JSP 声明加入到所有需要使用自定义标记库的页面中:
<%@ taglib uri="myTags" prefix="abc" %> |
注意指定的 uri
属性与在 web.xml 文件中指定的 taglib-uri
值相匹配。
为了进行动态引用,只需在所有需要使用这个库的页面中加入一个 JSP 声明即可:
<%@ taglib uri="/WEB-INF/lib/DateTagLib.tld" prefix="abc" %> |
在进行标记库的静态引用时,JSP 声明必须查询 web.xml 文件以执行库查询。这意味着如果移动或者重命名了库,或者希望在 web.xml 文件中加入更多的库,就必须停止服务器、更新 web.xml 文件、然后重新启动服务器。动态方法让 JSP 页直接指向 TLD 位置,因而是在解释 JSP 页面时进行处理。
静态方法提供了页面与库的实际名和位置之间一定程度的非直接性,这可以为您提供一些改变这些属性而不修改页面的灵活性。另一方面,动态方法提供了更大的灵活性,让您可以在运行时增加和移动标记声明。如果您对动态方法感兴趣,但是又担心做了一些改变后、有可能要更新多个页面的维护负担,那么您可以始终将 JSP 声明放到一个单独的 JSP 文件中,并在每一个要访问 Web 应用程序的自定义库的页面中加入这一页。这使您具有在运行时只需要更新信息一次就可以增加库的灵活性。
完成了所有这些准备工作后,我们就可以在 JSP 页面中使用这些自定义标记了。清单 3 显示了包含 DateTag
的 JSP 页面的浏览器输出:
<%@ taglib uri="/WEB-INF/lib/DateTagLib.tld" prefix="abc" %><HTML><HEAD><TITLE>Date tag example</TITLE></HEAD><BODY><H1>Date tag Example</H1><p>Hi today is <b><abc:displayDate /></b> </p></BODY></HTML> |
重新启动 Web 服务器并测试自己的 JSP 页面 !