JSP页面中的自定义标签
JSP页面中的自定义标签
用于调用Javaean组件中的操作和执行请求分派的标准JSP标签简化了JSP页面的开发和维护。JSP技术还提供了在自定义标签中封装其他动态功能的机制,这种自定标签是JSP语言的扩展。自定义标签通常是以标签库的形式出现的,它定义了一组相关的自定义标签,并包含实现这些标签的对象。
可以由自定义标签执行的任务包括对隐式对象的操作、处理表单、访问数据库和其他企业级服务,如电子邮件和目录、以及执行流程控制。JSP标签库是由精通Java编程语言和对访问数据库和其他服务非常熟悉的开发人员创建的,使用这些标签,Web应用程序开发人员就可以把注意力放到内容的呈现上,而不用费心考虑如何访问企业级服务。就像鼓励将库开发人员和库使用人员的工作分开一样,自定义标签通过封装反复执行的任务使它们可以在多个应用程序中重复使用,从而提高了生产率。
JSP技术社区给予标签库非常高的重视。有关标签库的信息和一些免费的库的地址,参见
http://java.sun.com/products/jsp/taglibraries.html
什么是自定义标签?
自定义标签是用户定义的JSP语言元素。当包含自定义标签的JSP页面转换为servlet时,这个标签就转换为一个名为tag handler的对象上的操作。之后当JSP页面的servlet执行时,Web容器就调用这些操作。
自定义标签有丰富的功能。它们可以
· 通过从调用页面传递的属性进行定制。
· 访问JSP页面可以使用的所有对象。
· 修改由调用页面生成的响应。
· 彼此通信。可以创建并初始化JavaBean组件、在一个标签中创建引用该bean的变量、再在另一个标签中使用这个bean。
· 彼此嵌套,可以在JSP页面中实现复杂的交互交互。
JSP页面示例
本章描述使用和定义标签所涉及的任务。本章用改写了的、在JSP页面示例中讨论的JSP版本的Duke’s Bookstore应用程序的部分演示这些任务,所做的改写利用了两个标签库的优点:Struts和tutorial-template。本章的第三节示例详细描述了两个标签:Strutst中的iterate和tutorial-template标签库中的一组标签。
Struts标签库提供了构建实现模型-视图-控制设计模式的国际化Web应用程序的框架。Struts包括完整的一组自定义工具标签,用于处理:
· HTML 表单
· 模板
· JavaBeans组件
· 逻辑处理
Duke's Bookstore应用程序使用Struts bean和logic子库中的标签。
Tutorial-template标签库定义了一组用于创建应用程序模板的标签。模板是带有占位符的JSP页面,这些占位符需要在每一屏幕中改变。每一个占位符称为模板的参数。例如,一个简单的模板可能包括在生成的屏幕上方的title参数,和一个JSP页面作为屏幕的定制内容的body参数。模板是用一组嵌入的标签创建的——definition、screen和parameter——它们用于构建Duke's Bookstore的屏幕定义表,并用insert标签将参数从表中插入屏幕。
图16-1显示了通过Duke's Bookstore Web组件的请求流程:
· template.jsp, template.jsp确定每一屏幕的结构。它使用insert标签用子组件组成屏幕。
· screendefinitions.jsp,它定义了每一屏幕使用的子组件。所有屏幕都有相同的横幅,但是标题和正文不同(由表15-1中的JSP页面列所指定)。
· Dispatcher,这是一个servlet,它处理请求并转发给template.jsp。
图16-1 通过Duke's Bookstore组件的请求流程
Duke's Bookstore应用程序的源代码位于在解压缩教程包(见运行示例)时生成的docs/tutorial/examples/web/bookstore3目录中。要编译、部署并运行这个例子,你需要:
1. 从以下地址下载Struts version 1.0.2
http://jakarta.apache.org/builds/jakarta-struts/release/v1.0.2/
2. 解压缩Struts并将struts-bean.tld、struts-logic.tld和struts.jar从jakarta-struts-1.0/lib拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore3。
3. 在终端窗口,进入<JWSDP_HOME>/docs/tutorial/examples/bookstore3.
4. 运行ant build。Build目标会进行所有必要的编译并将文件拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore3/build目标。
5. 确保已经启动了Tomcat。
6. 运行ant install。Install目标通知Tomcat 已经有了内容。
7. 如果还没有做的话,就启动PointBas数据库服务器并加入数据(见从Web应用程序中访问数据库)。
8. 打开书店URL http://localhost:8080/bookstore3/enter。
有关诊断常见问题的帮助见常见问题及其解决方案和故障排除。
使用标签
本节描述JSP页面如何使用标签,并介绍不同类型的标签。
要使用标签,页面编写者必须做以下两件事:
· 声明包含标签的标签库
· 让标签库实现对于Web应用程序可用
声明标签库
通过在使用任何自定义标签之前,将taglib指令加入页面中声明JSP页面将使用在标签库中定义的标签:
<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>
uri属性表示唯一标识标签库描述符(TLD)的URI,在标签库描述符中描述了uri。这个URI可以是直接或者非直接的。prefix属性定义了区分指定标签库所定义的标签与其他标签库提供的标签的前缀。
标签库描述符文件名必须有扩展名.tld。TLD文件储存在WAR的WEB-INF目录中,或者在WEB-INF的子目录中。可以直接或者间接引用TLD。
下面taglib指令直接引用一个TLD文件名:
<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>
这个taglib指令使用一个短的逻辑名间接引用TLD:
<%@ taglib uri="/tutorial-template" prefix="tt" %>
在Web应用程序部署描述符中将逻辑名映射到一个绝对位置。要将逻辑名/tutorial-template映射为绝对位置/WEB-INF/tutorial-template.tld,在web.xml中添加元素taglib:
<taglib>
<taglib-uri>/tutorial-template</taglib-uri>
<taglib-location>
/WEB-INF/tutorial-template.tld
</taglib-location>
</taglib>
让标签库实现可用
可以以两种方式让标签库实现对Web应用程序可用。实现了标签handler的类可以以非打包的形式储存在Web应用程序的WEB-INF/classes子目录中。另一种方法是,如果以JAR的形式发布库,就将它储存在Web应用程序的WEB-INF/lib目录中。在多个应用程序中共享的标签库储存在Java WSDP的<JWSDP_HOME>/common/lib目录中。
标签类型
JSP自定义标签是用XML语法编写的。它们有一个开始标签和结束标签,可能还有正文:
<tt:tag>
body
</tt:tag>
不带正文的自定义标签如下表示:
<tt:tag />
简单标签
一个简单标签没有正文,也没有属性:
<tt:simple />
带属性的标签
自定义标签可以带有属性。属性列在开始标签中,语法为attr="value"。像用参数定制方法的行为一样,属性值用于定制自定义标签的行为。在标签库描述符中指定标签属性的类型(见带属性的标签)。
可以用一个常量或者运行时表达式设置属性值。常量和运行时表达式与属性类型之间的转换过程遵循在设置JavaBean组件属性中描述的JavaBean组件属性规则。
Struts logic:present标签的属性决定是否对标签的正文进行判断。在下面的例子中,一个属性指定需要一个名为的参数Clear:
<logic:present parameter="Clear">
Duke's Bookstore应用程序页面catalog.jsp使用了运行时表达式设置属性的值,它决定Struts logic:iterate标签要枚举哪几本书。
<logic:iterate collection="<%=bookDB.getBooks()%>"
id="book" type="database.BookDetails">
带正文的标签
自定义标签可以包含自定义和核心标签、脚本元素、HTML文本和开始与结束标签之间的、依赖于标签的正文内容。
在下面的例子中,Duke's Bookstore应用程序页面showcart.jsp使用Struts logic:present标签清除购物车,并且如果请求包含一个名为Clear的参数就打印一个消息。
<logic:present parameter="Clear">
<% cart.clear(); %>
<font color="#ff0000" size="+2"><strong>
You just cleared your shopping cart!
</strong><br> <br></font>
</logic:present>
选择用属性或者正文传递信息
正如最后两节中所展示的,可以将给定的数据作为标签的属性或者标签的正文传递。一般来说,任何简单字符串或者可以由对简单表达式判断而生成的数据最好作为属性传递。
定义脚本变量的标签
自定义标签可以定义可在页面中的脚本中使用的变量。下面的例子展示了如何定义并使用包含一个从JNDI查询中返回的对象的脚本变量。这种对象的例子包括企业bean、事务、数据库、环境项等等:
<tt:lookupegin(); %> id="tx" type="UserTransaction"
name="java:comp/UserTransaction" />
<% tx.b
在Duke's Bookstore应用程序中,有几个页面使用了Struts的面向bean的标签以定义脚本变量。例如,bookdetails.jsp使用了bean:parameter标签以创建脚本变量bookId并设置它并将它设置为请求参数bookId的值。jsp:setProperty语句还设置bookDB对象的bookId属性为请求参数bookId的值。bean:define标签提取书店数据库bookDetails属性bookDetails的值并将结果定义为脚本变量book:
<bean:parameter id="bookId" name="bookId" />
<jsp:setProperty name="bookDB" property="bookId"/>
<bean:define id="book" name="bookDB" property="bookDetails"
type="database.BookDetails"/>
<h2><jsp:getProperty name="book" property="title"></h2>
操作标签
自定义标签可以通过共享对象彼此合作。
在下面的例子中,tag1创建了一个名为obj1的对象,再由tag2返回这个对象。
<tt:tag1 attr1="obj1" value1="value" />
<tt:tag2 attr1="obj1" />
在下面的例子中,由一组嵌套标签中的外围标签创建的对象对于所有内部标签都是可用的。因为没有为对象命名,所以可以减少潜在的命名冲突。这个例子展示在JSP页面中一组协作的嵌入标签会是什么样子的。
<tt:outerTag>
<tt:innerTag />
</tt:outerTag>
Duke's Bookstore页面template.jsp使用了一组协作标签定义应用程序的屏幕。在模板标签库中描述了这些标签。
定义标签
要定义标签,需要:
· 为该标签开发一个tag handler和helper类
· 在标签库描述符中声明这个标签
本节描述标签handler和TLD的属性,并解释如何为在前面几节中介绍的标签开发tag handler和库描述符元素。
标签handler
标签handler是由Web容器调用的一个对象,用于执行带有自定义标签的JSP页面时对这个标签进行判断。标签handler必须实现Tag或者BodyTag接口。接口可以用于接受现有Java对象并使它成为标签handler。对于新创建的处理器,可以用TagSupport和BodyTagSupport类作为基类。这些类和接口包含在javax.servlet.jsp.tagext包中。
JSP页面的servlet在对标签处理的不同阶段调用由Tag和BodyTag接口定义的标签handler。遇到自定义标签的开始标签时,JSP页面的servlet调用方法以初始化相应的handler,然后调用handler的doStartTag方法。遇到自定义标签的结束标签时,调用处理器的doEndTag方法。在标签handler需要与标签的正文交互时调用其他方法,见带正文的标签。为了提供标签handler的实现,必须实现在处理标签的不同阶段调用的方法,在表16-1中汇总了这些方法。
表16-1标签handler方法
标签handler类型
方法
简单
doStartTag, doEndTag, release
属性
doStartTag, doEndTag, set/getAttribute1...N, release
正文、判断且无交互
doStartTag, doEndTag, release
正文、迭代判断
doStartTag, doAfterBody, doEndTag, release
正文、交互
doStartTag, doEndTag, release, doInitBody, doAfterBody, release
标签handler可以使用一个能让它得以与JSP页面通信的API。到API的入口点是页面上下文对象(javax.servlet.jsp.PageContext),通过它标签handler可以获取JSP页面能够访问的所有其他隐式对象(请求、会话和应用程序)。
隐式对象可以有与其相关联的命名属性。可以用[set|get]Attribute方法访问这种属性。
如果标签是嵌入的,标签handler还可以访问与外围标签关联的handler称为parent)。
一组相关的标签handler类(标签库)一般是打包的且作为JAR文档部署。
标签库描述符
标签库描述符(TLD)是一个描述标签库的XML文档。TLD包含有关整个库以及库中包含的每一个标签的信息。Web容器用TLD验证标签,JSP页面开发工具也使用TLD。
TLD文件名必须有扩展名.tld。TLD文件也储存在WAR文件的WEB-INF目录中或者在WEB-INF的子目录中。
TLD必须以指定XML的版本和文档类型定义(DTD)的XML文档序言(prolog)开始。
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
Tomcat支持版本 1.1和1.2的DTD。不过,本章所讨论的是1.2版本,因为在开发的所有标签库中都应该使用最新的版本。模板库TLDtutorial-template.tld符合版本1.2。Struts库TLD符合版本1.1的DTD,它的元素要少,且其中一些元素使用了稍微不同的名字。
TLD的根是taglib元素。表16-2中列出了taglib的子元素:
表16-2 taglib子子元素
元素
说明
tlib-version
标签库的版本
jsp-version
这个标签库要求的JSP规范版本
short-name
JSP页面编写工具可以用来创建助记名的可选名字
uri
唯一标识该标签库的的URI
display-name
将由工具显示的可选名
small-icon
将由工具使用的可选小图标
large-icon
可被工具使用的可选大图标
description
可选的标签特定信息
listener
tag
listener元素
标签库可以指定一些事件监听器类(见处理Servlet生命周期事件)。这些监听器在TLD中作为listener元素列出,Web容器将初始化监听器类并以类似在WAR级定义的监听器的方式注册它们。与WAR级监听器不同,这里没有指定标签库监听器注册的顺序。listener元素的唯一子元素是listener-class元素,它必须包含监听类的完全限定名。
tag元素
库中的每一个标签都由给出其名字和其标签handler的类、在由标签创建的脚本变量上的信息以及标签属性上的信息描述。脚本变量信息可以在TLD中直接给出,也可以通过tag extra info类给出(见定义脚本变量的标签)。每一个属性声明包含指明属性是否是必需的、其值是否可以由请求时表达式确定以及属性类型的内容(见属性元素)。
在tag元素中的TLD中指定标签。在表16-3中出了tag的子元素:
表16-3 标签子元素
元素
说明
name
唯一标签名
tag-class
标签handler类的完全限定名
tei-class
javax.servlet.jsp.tagext.TagExtraInfo的可选子类。见提供有关脚本变量的信息。
body-content
正文内容类型。见body-conten元素和 body-content元素。
display-name
由工具显示的可选名
small-icon
可以由工具使用的小图标
large-icon
可以由工具使用的大图标
description
可选的标签特定的信息
variable
可选的脚本变量信息。见提供有关脚本变量的信息。
attribute
标签属性信息。见Attribute 元素。
下面几节描述开发在标签类型中介绍的每一种类型的标签所需要的方法和TLD。
简单标签
标签handler
简单标签的handler必须实现Tag接口的doStartTag和doEndTag方法。在遇到开始标签时调用doStartTag方法。因为简单标签没有正文,所以这个方法返回SKIP_BODY。在遇到结束标签时调用doEndTag方法。如果要对页面的其他部分进行判断,则doEndTag方法需要返回EVAL_PAGE,否则,它就返回SKIP_PAGE。
在第一节讨论的简单标签
<tt:simple />
由下列标签handler实现:
public SimpleTag extends TagSupport {
public int doStartTag() throws JspException {
try { pageContext.getOut().print("Hello.");
} catch (Exception ex) {
throw new JspTagException("SimpleTag: " +
ex.getMessage()); }
return SKIP_BODY; }
public int doEndTag() {
return EVAL_PAGE;
}
}
body-content元素
没有正文的标签必须用body-content元素声明它们的正文内容是空的:
<body-content>empty</body-content>
JSP页面中的自定义标签
816-7869-10