自定义标签基础知识
1.自定义标签(Custom Tag)概述
2.标签的形式
<前缀:标签名 属性名1=值1属性名2=值2 …>标签主体
一个标签可以没有属性或者没有主体,也可以同时没有属性和主体。没有主体的标签可以简写成这样的形式:<前缀:标签名 属性名1=值1 … />、<前缀:标签名 />。
3.自定义标签的优点
4.自定义标签库(Custom Tag library)
由一系列功能相似、逻辑上互相联系的自定义标签构成的集合称为标签库。
5.创建自定义标签的方式
(1)Java类文件方式。
(2)标签文件方式。
标签文件是一个扩展名为.tab的文件,它必须存储在Web应用程序的WEB-INF目录中。标签文件的作用类似标签处理文件,它是一个包含一些内容或JSP代码的要重用的JSP片段。在JSP页面遇到自定义标签时,它会转到标签文件以执行标签定义。不需要一个单独的标签库描述符,因为标签文件包含自定义标签的完整实现。
Java类文件方式创建和使用一个自定义标签库的基本步骤有如下几步。
(1)创建标签的处理类(Tag Handle Class)。定义标签的行为,并在JSP引擎遇到自定义标签时调用执行。标签处理类是一个Java类,这个类继承了TagSupport或者扩展了SimpleTag接口,通过这个类可以实现自定义JSP标签的具体功能。
(2)创建标签库描述文件TLD(Tag Library Descrptor File)。描述标签库的XML文档,向JSP引擎提供有关自定义标签的标签处理程序的信息。
(3)在web.xml文件中声明TLD的位置。
(4)在JSP文件中用taglib指令引入标签库。
(5)在JSP中使用标签库标签。
6.创建标签的处理类
(1)传统标签处理器在javax.servlet.jsp.tagext包中的接口和类,如图8-1所示。
图8-1 传统标签的接口和类
我们要写的自定义标签处理类主要继承自TagSupport、BodyTagSupport这两个类。通过继承这两个类,只需要重新定义那些需要自定义的行为的方法,从而简化了标签处理程序的开发。
(2)简单标签处理器javax.servlet.jsp.tagext包中的接口和类。我们要写的自定义标签处理类继承自SimpleTagSupport类,重写doTag()方法。这是在JSP2.0新规范中增加的类,这个类实现自SimpleTag接口,之所以称其为“简单”是指它相对于传统标签处理器实现任务要简单得多。
(3)TagSupport类。TagSupport类并不能对标签主体的内容做任何处理,您所能决定的只是是否显示主体的内容。
① 生命周期
当JSP容器在解释JSP页面时,如果碰到自定义标签,将利用“标签处理类”建立一个“标签处理对象”。在建立“标签处理对象”的过程中,JSP容器会根据自定义标签的属性值来初始化“标签处理对象”的属性。
首先,JSP容器会运行doStartTag()方法内的程序代码,然后根据此方法的返回值决定后续动作。如果返回SKIP_BODY,表示要求JSP容器忽略标签主体的内容;如果返回EVAL_BODY_INCLUDE,表示要求JSP容器要显示标签主体的内容,然后运行doAfterBody()方法。
如果doAfterBody()方法传回EVAL_BODY_AGAIN,表示要求JSP容器再次显示标签主体的内容,如果返回SKIP_BODY,JSP容器将会运行doEndTag()方法。
最后,JSP容器会运行doEndTag()方法内的程序代码,并根据此方法的返回值决定后续动作:如果返回SKIP_PAGE,JSP容器会运行release()方法,然后忽略自定义标签以后的JSP内容;如果返回EVAL_PAGE,JSP容器会先运行release()方法,然后运行自定义标签以后的JSP内容。
② 生命周期主要方法
● doStartTag()方法可返回的值有SKIP_BODY,忽略标签主体的内容(此方法的预设返回值);VAL_BODY_INCLUDE,表示要求JSP容器要显示标签主体内容。
● doAfterBody()方法可返回的值有以下几项。
● SKIP_BODY:要求JSP容器忽略主体内容,进入标签处理程序的下一步工作(此方法的预设返回值)。
● EVAL_BODY_AGAIN:要求JSP容器再次显示标签主体内容。
● doEndTag()方法可返回的值有以下几项
● SKIP_PAGE:忽略自定义标签以后的JSP网页内容。
● EVAL_PAGE:运行自定义标签以后的JSP网页内容(此方法的预设返回值)。
● release():释放标签处理对象所占用的系统资源。
(4)BodyTagSupport类。
① 生命周期
BodyTagSupport与TagSupport的区别主要是标签处理类是否需要读取标签体的内容和改变标签体返回的内容,如果不需要交互的就用TagSupport,否则就用BodyTagSupport。
这里需要注意的是TagSupport也可以有体,如果将TagSupport理解成是没有体的标签,而将BodyTagSupport理解成是有体的标签就错了。当然用TagSupport实现的标签,都可以用BodyTagSupport来实现,道理很简单,因为BodyTagSupport继承了TagSupport。
② 生命周期主要方法
● doStartTag() 方法可返回的值有SKIP_BODY,表示要求JSP容器忽略主体内容;
EVAL_BODY_INCLUDE,表示要求JSP容器要显示标签主体内容;EVAL_BODY_BUFFERED 表示JSP容器会将标签主体的处理结果建立成一个BodyContent对象(此方法的预设返回值)。
● setBodyContent()方法会设置BodyContent对象的一些属性,包括标签主体的内容和在处理标签过程中要输出至response的数据对象。
● doInitBody()方法在第一次处理标签主体内容时,它将对主体进行初始化的工作。
● doAfterBody()方法可返回的值有SKIP_BODY,表示要求JSP容器忽略主体,进入下一步的处理工作;EVAL_BODY_AGAIN,表示要求JSP容器再次处理标签主体。
● doEndTag()方法可返回的值有SKIP_PAGE,表示忽略自定义标签以后的JSP网页内容; EVAL_PAGE,表示运行自定义标签以后的JSP网页内容。
● release()方法释放标签处理对象所占用的系统资源。
(5)SimpleTagSupport类。
生命周期事件有以下几种。
● 当JSP容器遇到标记时,将标签处理类的默认构造方法建立一个标签处理实例。注意必须为每个标记都创建一个新的实例,这很重要。
● 在标记处理程序中调用setJspContext()和setParent()方法。如果传递的值是“null”,则不需要调用setParent()方法。在使用标记文件的情况下,创建一个JspContext包装,以便标记文件看上去具有其本身的页面范围。调用getJspContext()必须返回所包装的JspContext。
● 容器为每个标记所定义的属性,同时也需要提高set方法,set方法是用来让外界设置属性的值,其顺序是它们出现在JSP页或标记文件中的顺序。如果属性值是表达式语言的表达式或运行时表达式,则它首先被赋值,然后被传递到设置器;另一方面,如果属性为动态属性,则调用setDynamicAttribute()。
● 由容器调用setJspBody()方法,将该标记的主体设置为JspFragment;如果标记被声明为具有空的值,则将null值传递到setJspBody()。
● 由容器调用doTag()方法。所有的标记逻辑、迭代、主体赋值等都在该方法中发生。
● 在doTag()方法返回后,所有的变量被同步。
7.撰写标签处理类
你所撰写的标签处理类应该具备下列特性:
● 承TagSupport类或BodyTagSupport类或SimpleTagSupport类;
● 对自定义标签的属性声明相对应的变量,并提供一个setXXX()方法;
● doStartTag()、doAfterBody()或doEndTag()方法(或doTag()方法)内撰写Java程序代码,实现自定义标签欲提供的功能。
示例如下:
[java] view plaincopy
Welcome.java
package com.mapgis.vfd.taglib;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.JspWriter;
import java.io.IOException;
public class Welcome extends BodyTagSupport {
public int doAfterBody() {
try {
//获得标签体的内容
BodyContent bodyContent = super.getBodyContent();
String bodyString = bodyContent.getString();
//获取输出流对象
JspWriter out = bodyContent.getEnclosingWriter();
out.print(bodyString.toUpperCase());
bodyContent.clear();
} catch (IOException e) {
System.out.println("BodyContentTag.doAfterBody() 中出现错误" + e.getMessage());
e.printStackTrace();
}
return this.SKIP_BODY;
}
}
[xhtml] view plaincopy
8.创建标签库描述文件
标签库描述文件(Tag Library Discriptor,TLD)是一份标准的XML文件,用来记录一个标签库内拥有哪些标签?每个标签包含哪些属性?取得这些信息后,JSP容器才能正确地处理并运行JSP所包含的自定义标签。以下是一个JSP2.0规范的TLD:
weclome.tld
<?xml version="1.0" encoding="UTF-8"?>
<!-- 标签库描述符 -->
<taglib 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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 此标签库的一个描述 -->
<description>A tag library exercising SimpleTag handlers.</description>
<!-- 此标签库的版本,由程序设计师自行决定(必须) -->
<tlib-version>1.0</tlib-version>
<!-- 定义一个简短的名称(必须)主要由工具使用 -->
<short-name>SimpleTagLibrary</short-name>
<!-- 定义该标签库的唯一uri名(在taglib指令中使用) -->
<uri>/SimpleTagLibrary</uri>
<tag><!-- 定义每个标签的特性 -->
<description>body content to Upper</description><!-- 描述 -->
<name>simpletag</name> <!-- 自定义标签的名称 -->
<tag-class>xmh.comchapter11.Welcome</tag-class> <!-- 此标签对应的处理类 -->
<body-content>scriptless</body-content><!-- 定义标签主体的种类:指定标签的格式,empty表示标签没有标签主体;JSP表示标签的标签体中可以包含JSP代码;scriptless表示标签主体中可以包含EL表达式和JSP的动作元素,但不能包含JSP的脚本元素;tagdependent表示标签的标签主体交由标签本身去解析处理。如果指定为它,那么你在标签体中所写的任何代码都会原封不动地传给标签处理器,而不是传递执行的结果。 -->
<attribute>
<name>attrName</name> <!-- 此属性的名称 -->
<required>false</required> <!-- 此属性是否为必要:true | false -->
<rtexprvalue>true</rtexprvalue><!-- 属性值是否可以在JSP运行时期动态产生:true | false -->
</attribute>
</tag>
</taglib>
9.在web.xml文件中声明TLD的位置
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
......
<jsp-config>
<taglib>
<!-- 指定TLD文件所对应的URI -->
<taglib-uri>/mytag</taglib-uri>
<!-- 指定TLD文件的存放路径 -->
<taglib-location>/WEB-INF/tlds/weclome.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
注意在JSP2.0规范中可以不用声明TLD的位置,因为容器开始建立<uri>和TLD映射时,会首先在web.xml中查找,看是否有<taglib>项,如果确实有,就会使用这些设置来帮助建立映射。否则会在WEB-INF目录以及它的tlds子目录(还可以是jar文件中)下查找TLD文件,然后自动建立TLD文件和<uri>名之间的映射关系。
10.在JSP文件中用taglib指令引入标签库
<%@ taglib uri="/mytag" prefix="my" %>
11.在JSP中使用标签库标签
<my:simpletag>Hello,World!</my:simpletag>
12.自定义JSP标签的处理过程
<!--[if !supportLists]-->(1)在JSP中引入标签库:<% @ taglib prefix="taglibprefix" uri="tagliburi" %>。
<!--[if !supportLists]-->(2)在JSP中使用标签库标签:<prefix : tagname attribute="tagattribute">。
<!--[if !supportLists]-->(3)Web容器根据第二个步骤中的prefix,获得第一个步骤中声明的taglib的uri属性值。
<!--[if !supportLists]-->(4)Web容器根据uri属性在web.xml找到对应的<taglib>元素。
<!--[if !supportLists]-->(5)从<taglib>元素中获得对应的<taglib-location>元素的值。
<!--[if !supportLists]-->(6)Web容器根据<taglib-location>元素的值从WEB-INF/目录下找到对应的.tld文件。
<!--[if !supportLists]-->(7)从.tld文件中找到与tagname对应的<tag>元素。
<!--[if !supportLists]--><!--[endif]-->(8)从<tag>元素中获得对应的<tag-class>元素的值。
<!--[if !supportLists]-->(9)Web容器根据<tag-class>元素的值创建相应的标签处理类的实例。
<!--[if !supportLists]-->(10)Web容器调用这个实例的doStartTag/doAfterBody/doEndTag方法完成相应的处理。