xmlns:
xsi="http:
//www.w3.org/2001/XMLSchema-instance"
xsi:
schemaLocation="
version="2.0">
--标签库的配置信息-->
自定义标签库
1.1
ct
--random标签的配置信息-->
产生一个指定范围的随机数
random
chapter1.RandomTag
empty
min
false
false
max
false
false
上面的配置代码分为三部分:
标签库描述符文件头、标签库配置信息和random标签的配置信息。
其中描述库文件头的内容并不需要读者去记忆,读者只需要将JSTL中的任何一个TLD文件打开,将其中的标签库描述文件头部分复制过来即可。
如果在标签库描述符文件中包含中文,需要将encoding属性改成“UTF-8”或“GBK”。
标签库的URI被元素指定为“”,该标签值就是taglib指令的uri属性值。
元素指定了标签库的默认前缀。
要注意的是,该默认前缀并不等于taglib指令的prefix属性值,也就是说,taglib指令的prefix属性值和元素的值毫无关系。
元素值实际上只是个推荐的标签库前缀,如国际化标签库的描述符文件(fmt.tld)中的元素值是“fmt”。
在使用taglib指令引用某个标签库时,应尽量使用元素推荐的标签库前缀,当然,也可以设置其他的前缀名。
random标签的标签体类型为“empty”(元素的值),表示该标签不支持标签体。
关于元素支持的其他值,将在后面的部分详细介绍。
random标签的min属性和max属性都被设置可选的属性,并且都不支持动态属性值。
因此,在JSP页面中设置random标签的这两个属性时只能直接为它们赋值。
4.测试random标签
在\chapter1目录建立一个random.jsp文件(在本章的所有JSP文件都放在该目录下),并输入如下的内容:
<%@pagelanguage="java"contentType="text/html;charset=UTF-8"%>
<%@tagliburi=""prefix="ct"%>
<%@tagliburi="prefix="c"%>
--使用forEach标签产生1个10至200之间的随机数-->
forEachbegin="1"end="1">
--调用random标签-->
randommin="10"max="200"/>
forEach>
在浏览器地址栏中输入如下的URL:
http:
//localhost:
8080/demo/chapter1/random.jsp
浏览器显示的输出结果如图1.1所示。
图1.1使用random标签产生1个10至200之间的随机数
5.程序总结
在部署和安装自定义标签时,TLD文件应放在WEB-INF目录或其子目录中(包括classes和lib目录)。
根据本例实现的random标签,可以将开发自定义标签的基本步骤总结如下:
实现一个标签类。
该标签类可以实现Tag接口、继承TagSupport类或实现其他的接口(这些接口将在后面的部分详细介绍)。
在标签库描述符文件(TLD文件)中配置自定义标签。
部署和安装自定义标签。
主要是将.class文件放在WEB-INF\classes目录中,并且将TLD文件放在WEB-INF目录或其子目录中。
1.1.2自定义标签能做什么
自定义标签除了可以读取标签的属性值外,还可以完成如下的工作:
单次执行标签体中的内容、重复执行标签体中的内容。
修改标签体中的内容。
忽略JSP页面中位于自定义标签后面的内容。
在上一节介绍了Tag接口中的doStartTag方法,该方法在Web容器执行到自定义标签的开始标记时被调用。
除了这个方法,在Tag接口中还有doEndTag方法,该方法在Web容器中执行到自定义标签的结束标记时被调用。
doStartTag方法可以通过返回如下两个值来控制Web容器是否执行自定义标签的标签体:
EVAL_BODY_INCLUDE:
执行自定义标签的标签体。
SKIP_BODY:
忽略(不执行)自定义标签的标签体。
doEndTag方法可以通过返回如下两个值来控制Web容器是否忽略JSP页面中位于自定义标签后面的内容:
EVAL_PAGE:
继续执行自定义标签后面的内容。
SKIP_PAGE:
忽略自定义标签后面的内容。
其中EVAL_BODY_INCLUDE、SKIP_BODY、EVAL_PAGE和SKIP_PAGE是在Tag接口中定义的整型常量,所有实现Tag接口的类都可以直接使用这些常量。
除此之外,实现IterationTag接口的标签类还可以重复执行标签体。
IterationTag是Tag接口的子接口。
在IterationTag接口中有一个doAfterBody方法,该方法可以通过返回如下两个值来决定是否重复执行自定义标签体的内容:
EVAL_BODY_AGAIN:
重复执行标签体的内容。
SKIP_BODY:
不再执行标签体的内容。
其中EVAL_BODY_AGAIN是在IterationTag接口中定义的整型常量。
如果doAfterBody方法返回SKIP_BODY,Web容器会继续执行自定义标签的结束标记,同时会调用doEndTag方法。
从上面的描述可以将Web容器执行自定义标签的过程总结如下:
1.Web容器首先会执行自定义标签的开始标记,同时会调用标签类的doStartTag方法。
2.如果doStartTag方法返回EVAL_BODY_INCLUDE,Web容器在执行完标签体的内容后,会调用标签类的doAfterBody方法;如果doStartTag方法返回SKIP_BODY,doAfterBody方法不会被调用,Web容器会直接调用标签类的doEndTag方法。
3.如果doAfterBody方法被调用,并且该方法返回EVAL_BODY_AGAIN,Web容器会再次执行标签体的内容;如果doAfterBody方法返回SKIP_BODY,Web容器会调用标签类的doEndTag方法。
4.如果doEndTag方法返回EVAL_PAGE,Web容器会执行自定义标签后面的内容;如果doEndTag方法返回SKIP_PAGE,Web容器会忽略自定义标签后面的内容。
1.1.3自定义标签API
自定义标签API中除了前面介绍的Tag接口和IterationTag接口外,还有另外三个核心接口:
JspTag、
BodyTag和SimpleTag。
为了简化自定义标签的编程工作,在自定义标签API中提供了TagSupport类、SimpleTagSupport类和BodyTagSupport类。
其中TagSupport类实现了IterationTag接口,SimpleTagSupport类实现了SimpleTag接口,BodyTagSupport类是TagSupport的子类,并实现了BodyTag接口。
上述五个接口和三个类的关系如图1.2所示。
图1.2自定义标签API的核心接口和类的关系图
上述的接口和类都在javax.servlet.jsp.tagext包中,读者可以从如下的网址来查询这些接口和类的详细介绍:
访问上面的网址将得到如图1.3所示的页面。
图1.3自定义标签API的官方文档
上述五个接口的功能和作用如下:
1.JspTag接口
JspTag接口是所有自定义标签的父接口。
它没有任何属性和方法。
Tag接口和SimpleTag接口是JspTag的两个直接子接口。
其中JspTag和SimpleTag是JSP2.0新增的接口。
在JSP2.0之前的版本的所有自定义标签的父接口是Tag。
因此,可以将所有实现Tag接口的自定义标签称为传统标签,把所有实现SimpleTag接口的标签称为简单标签。
在本章主要介绍传统标签,简单标签将在下一章详细介绍。
2.Tag接口
Tag接口是所有传统标签的父接口。
该接口有两个核心方法(doStartTag和doEndTag)以及四个常量(EVAL_BODY_INCLUDE、SKIP_BODY、EVAL_PAGE和SKIP_PAGE)。
其中doStartTag方法可以返回EVAL_BODY_INCLUDE和SKIP_BODY,用于控制Web容器是否执行标签体的内容;doEndTag方法可以返回EVAL_PAGE和SKIP_PAGE,用于控制Web容器是否执行自定义标签后面的内容。
3.IterationTag接口
IterationTag接口继承了Tag接口。
IterationTag接口可用于实现需要循环执行标签体内容的自定义标签。
在IterationTag接口中只有一个doAfterBody方法和一个EVAL_BODY_AGAIN常量。
doAfterBody方法通过返回EVAL_BODY_AGAIN常量或Tag接口中的SKIP_BODY常量来控制Web容器是否重复执行标签体中的内容。
doStartTag方法、doAfterBody方法和doEndTag方法的调用关系详见1.1.2节中的介绍。
并不是每一个自定义标签都需要循环执行标签体的内容,或是控制自定义标签后面的内容是否被执行。
因此,JSPAPI中提供了一个实现IterationTag接口的TagSupport类。
在TagSupport类中对Tag接口和IterationTag接口中定义的方法都提供了默认的实现。
如doStartTag方法、doEndTag方法和doAfterBody方法都提供了默认的返回值,代码如下:
publicclassTagSupportimplementsIterationTag,Serializable
{
publicTagSupport(){}
publicintdoStartTag()throwsJspException
{
returnSKIP_BODY;
}
publicintdoEndTag()throwsJspException
{
returnEVAL_PAGE;
}
publicintdoAfterBody()throwsJspException
{
returnSKIP_BODY;
}
//此处省略了TagSupport类中的其他方法和属性
......
}
标签类通过继承TagSupport类,就不需要实现Tag接口和Iteration接口中的每一个方法了,这样将大大简化自定义标签的开发工作。
4.BodyTag接口
BodyTag接口继承了IterationTag接口。
BodyTag接口不仅拥有IterationTag接口的所有功能,而且还可以初始化和修改标签体的内容。
在BodyTag接口中定义了两个方法(doInitBody和setBodyContent)和两个常量(EVAL_BODY_BUFFERED和EVAL_BODY_TAG),这两个常量的含义相同。
其中EVAL_BODY_TAG常量是在JSP1.2中的遗留产物,在JSP的后续版本中可能不支持该常量,因此,建议使用EVAL_BODY_BUFFERE常量。
如果doStartTag方法返回EVAL_BODY_BUFFERE,Web容器就会将标签体的执行结果保存在BodyContent对象中,然后Web容器在处理标签时会调用标签类的setBodyContent方法将BodyContent对象传入标签类的对象实例,接下来就可以在标签类的对象实例中处理标签体的执行结果了。
由于BodyTag接口及其父接口中定义了很多方法,为了在实现BodyTag接口的类中不用再实现所有的方法,JSPAPI提供了一个BodyTagSupport类,该类是TagSupport类的子类,并且实现了BodyTag接口。
在BodyTagSupport类中改变了doStartTag方法的默认返回值,并且覆盖了其他的核心方法,代码如下:
publicclassBodyTagSupportextendsTagSupportimplementsBodyTag
{
protectedBodyContentbodyContent;
publicBodyTagSupport()
{
super();
}
publicvoidsetBodyContent(BodyContentb)
{
this.bodyContent=b;
}
publicBodyContentgetBodyContent()
{
returnbodyContent;
}
//改变了doStartTag方法的默认返回值
publicintdoStartTag()throwsJspException
{
returnEVAL_BODY_BUFFERED;
}
publicintdoEndTag()throwsJspException
{
returnsuper.doEndTag();
}
publicvoiddoInitBody()throwsJspException
{
}
publicintdoAfterBody()throwsJspException
{
returnSKIP_BODY;
}
//此处省略了BodyTagSupport类的其他方法和属性
......
}
从前面的内容可知,自定义标签API涉及到了三个方法(doStartTag、doAfterBody和doEndTag)以及这三个方法可能返回的六个常量(EVAL_BODY_INCLUDE、EVAL_BODY_BUFFERE、SKIP_BODY、EVAL_BODY_AGAIN、EVAL_PAGE和SKIP_PAGE)。