JSP Tag Library。JSP Tag
Library简介
跟javabean类似,不过它在处理网页内容方面更方便。
它使用taglib指令在JSP网页中使用。taglib有两个属性uri(用来标示tld(标签描述)文件的位置)和prefix(标签的前缀,用来区分多个标签,尤其是同名的时候)。
tld文件中包含标签的名称简述属性以及它的Handler
Class。
当在编译JSP时,如果遇到taglib指令,会检查是否已经加载它的tld文件,如果没有则加载至jvm。
—简单的Tag
Library范例
通常完整的标签函数程序需要三个组件:
1标签处理类(Handler Class)
2标签性质文件(tld
file)
3jsp网页
1 标签库的Handler
Class一般要导入以下两个包:
javax.servlet.jsp.*和javax.servlet.jsp.tagext.*
Handler
Class要继承TagSupport类,如:
import javax.servlet.jsp.*;
import
javax.servlet.jsp.tagext.*;
public class TagLibTest extends TagSupport
2 创建tld文件
<?xml version=”1.0″ encoding=”UTF-8″?>
<taglib
version=”2.1″ xmlns=”http://java.sun.com/xml/ns/javaee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd
“>
<description>first taglib
test</description>
<tlib-version>1.0</tlib-version>
<short-name>taglibtest</short-name>
<tag>
<description>a
very simple tag lib
test</description>
<name>Test</name>
<tag-class>test.TagLibTest</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
3
在jsp网页中使用taglib引入标签:
<%@ taglib uri=”/WEB-INF/tlds/taglibtest.tld”
prefix=”tagtest”%>
然后可以这样使用此标签:<tagtest:Test/>
也可以不直接在taglib指令中引用tld文件,而是可以通过web.xml文件设置taglib来为tld文件设置一个别名,这样一是可以不用输入过长的路径,二是当在项目中有多个地方引用同一tld文件的时候,当tld文件位置发生变化的时候,只需要修改web.xml文件即可。
如:
<jsp-config>
<taglib>
<taglib-uri>tltest</taglib-uri>
<taglib-location>/WEB-INF/tlds/taglibtest.tld</taglib-location>
</taglib>
</jsp-config>
然后在jsp网页则可以这样引用此tld文件:<%@
taglib uri=”tltest” prefix=”ttest”%>
—标签处理类(Tag Handler
Class)
编译jsp网页的时候,遇到自定义标签的时候,服务器会先到tld文件查找该标签的标签处理类,然后将数据传送到标签处理类进行处理。
API概观:
在使用标签库的时候必须要引用两个包:
javax.servlet.jsp.*和javax.servlet.jsp.tagext.*
在javax.servlet.jsp中有六个主要的类
ErrorData,JspContext(抽象类,给它的子类提供一些便利方法,如管理所有范围的变量,使用JspWriter输出),JspEngineInfo(抽象类,提供Container的信息。如它的getSpecificationVersion方法来取得当前的
Container的版本),JspWriter(out隐含对像就是此类)和PageContext(pageContext隐含对像的类型)。具体的
api参见java文档。
javax.servlet.jsp.tagext中有7个接口:
BodyTag,Tag,IterationTag;JspTag,SimpleTag;DynamicAttributes,TryCatchFinally(主要用来处理标签处理的时候出现的异常)。
一共有17类,主要的两个类:TagSupport和BodyTagSupport,它们之间的差别在于BodyTagSupport类是在标签处理类要处理标签本体内容的时候使用,反之则使用TagSupport
—TagSupport类
主要实现了接口Tag,IterationTag,JspTag和Serializable。
主要方法为doStartTag,doAfterBody,doEndTag,release。
doStartTag:
当编译到自定义标签的时候会先调用doStartTag方法,它会返回两个整数值Tag.SKIP_BODY(忽略本体内容直接执行doEndTag方法)和Tag.EVAL_BODY_INCLUDE(只会在web网页上正常的显示本体内容,但标签类不会对本内容做任何运算和处理)
doAfterBody:
这个方法是用来重复执行(正常显示)标签本体内容的。它会返回两个值,除了Tag.SKIP_BODY,还有
IterationTag.EVAL_BODY_AGAIN(表示重复调用本体内容,并再度调用doAfterBody方法直到此方法返回
Tag.SKIP_BODY)
doEndTag:
这是处理自定义标签的结束标签的(如:</tag>),它也会返回两个值:Tag.SKIP_PAGE(表示jsp网页的执行就到此为止,后面的都不再执行,已经执行完的内容马上传回到客户端的浏览器上)和Tag.EVAL_PAGE(表示jsp网页继续执行)
release:
将标签处理类所产生或获得的资源都释放,并且重新设定标签处理类的初始状态,然后这个标签类再放至资源池,等待下一次使用。
另外自定义标签处理类也有javabean的setter和getter机制。
使用有属性的标签如:<prefix:tag
a1=”v1″/>必须在标签处理类中添加setter和getter方法,并且在tld文件中进行设置
在标签处理类中,则可以使用如下代码来取得或设定a1属性的值。
private
int a1;
public void setA1(int value)
{
this.a1 = value;
}
public
int getA1()
{
return
this.a1;
}
—BodyTagSupport类
此类继承TagSupport类,并实现了BodyTag接口。它和TagSupport类最大的区别在于它能处理本体内容。它除了有TagSupport的方法之外,还增加了getBodyContent,setBodyContent,doInitBody方法。
它的doStartTag方法除了返回TagSupport类的同名方法返回的两个整数值之外,它还增加了一个
BodyTag.EVAL_BODY_BUFFERED(标签内的本体内容要被处理,并且处理的结果必须存储在BodyContent类中,当返回此值的时候,标签的本体内容如果不被程序处理,将不会显示)。如果返回的值是BodyTag.EVAL_BODY_INCLUDE,使用
getBodyContent()将只会得到null。
要处理标签的本体内容,首先要产生(若之前执行过,直接从资源池中取得)BodyContent类的实例,调用setBodyContent,然后再调用
doInitBody(调用此方法的时候,BodyContent还没有得到标签的本体内容,但是在此方法中对BodyContent写入(比如使用
BodyContent的println等方法)的内容,将被添加在标签本体内容之前)方法,让用户来新增初始值。处理完标签的本体内容后,则调用
doAterBody(对标签的本体内容的处理要放在这个方法中,因为只有执行到这个方法的时候BodyContent才得到了标签的本体内容)方法,此方法和TagSupport的doAfterBody一样,只是其返回值也可以是BodyTag.EVAL_BODY_AGAIN(其实和
IterationTag类的一样)。
当其返回BodyTag.EVAL_BODY_AGAIN的时候的工作流程:
doInitBody方法只在处理本体内容前调用一次,返回BodyTag.EVAL_BODY_AGAIN的时候,不会再调用此方法。每次返回
BodyTag.EVAL_BODY_AGAIN后,会将本体内容再次附加在之前的BodyContent的尾部,比如本体内容为abc,则第二次在
doAfterBody方法中得到的本体内容就为abc,abc,。
标签处理类实例的重用机制:
只有在使用相同的自定义标签的属性的时候才会重用标签处理类的实例,而且重用的实例只会调用属性值发生变化的属性的设置方法。如:<p:tag
attr1=”1″/>,<p:tag attr1=”1″ attr2=”2″/>,<p:tag attr1=”1″
attr2=”3″/>,第一个标签和后两个标签使用不同的标签处理类实例,因为后两个标签使用了attr2属性。后两个标签使用同一个标签处理类实例,但是最后一个标签会调用attr2的设置方法,但不会调用attr1的,因为attr1的值没有变,attr2的值变了。
在编写标签处理类的时候,一般有三件事情需要做:
为可选的属性设置默认值
重设每次调用的状态:因为处理类的实例有可能被重用,所以有的时候需要重设处理类的状态,不然当被重复调用的时候可能会发生错误。这个可以在doStartTag方法中进行,也可以实现TryCatchFinally接口,在doFinally方法中来处理。
为标签处理类实例保留昂贵的资源:这种资源对像的释放,放在release方法中(这个方法只会在此实例要被垃圾回收前调用一次)。
—TagExtraInfo和VariableInfo
这个是用来设置标签生成或处理的脚本变量的。这个只是一种可选的途径,它的使用目的和效果可以通过使用jsp作用域中的变量来达到和实现。使用自定义标签生成脚本变量会因为生成额外的代码会带来多余的开销,而且在将jsp转成servlet的时候由于要和容器产生的其他代码有复杂的交互而产生其他的潜在问题,所以最好考虑清楚是否需要使用自定义标签来生成脚本变量。
使用TagExtraInfo的子类来生成一个脚本变量的方式和代码为:
先创建一个TagExtraInfo的子类
public
class TEI extends TagExtraInfo
{
//实现这个方法来告诉容器关于生成变量的一些信息,
public
VariableInfo[] getVariableInfo(TagData
data)
{
//变量的信息,注意第三个布尔参数,如果设为false,则在jsp中必须在使用此自定义标签前,声明一个叫v1的
//String类型的变量,如果是true,则标签会创建一个叫v1的String类型的变量,在使用此标签前不要声明叫v1的
//变量,最后一个参数是这个变量的有效范围,AT_BEGIN是从标签的起始标签到jsp网页结束,AT_END是从标签的结
//束到jsp网页结束,NESTED则代表从标签的起始到标签结束。
VariableInfo info = new VariableInfo(“v1″,
“String”, true, VariableInfo.AT_BEGIN);
VariableInfo[] infos =
{info};
return
infos;
}
}
然后设置tld文件:
<tag>
<description>a very
simple tag lib
test</description>
<name>Test</name>
<tag-class>test.TagLibTest</tag-class>
<tei-class>test.TEI</tei-class>
<body-content>JSP</body-content>
<attribute>
<name>cont</name>
<required>true</required>
</attribute>
</tag>
和平常的tag节点比,在tag节点中添加了一个<tei-class>节点。
最后在jsp网页使用以下代码调用生成的变量:
<%@
taglib uri=”/WEB-INF/tlds/taglibtest.tld” prefix=”tagtest”%>
<%
pageContext.setAttribute(“v1″, “this is xxxx”);
//String v1 =
“”;//如果VariableInfo的构造函数的第三个参数使用false,则需要声明此变量,否则不能声明此变量否则会发生错误
%>
<tagtest:Test
cont=”this is an attribute”>
<br>this is a tagextrainfo
test<br>
<%out.println(“value is ” +
v1);%>
</tagtest:Test>
最后在jsp网页中应该显示:
this is a tagextrainfo
test
value is
xxxx
还可以在tld文件中进行变量的设置,而不需要写继承自TagExtraInfo的类来设置变量:
<variable>
<!–也可以使用name-from-attribute节点,从标签的属性中取得变量名,其他的设置和VariableInfo构造函数一样
–>
<name-given>v1</name-given>
<variable-class>String</variable-class>
<declare>true</declare>
<scope>NESTED</scope>
</variable>
使用variable设置,就不能使用<tei-class>test.TEI</tei-class>节点。
一般只有在变量的类型或是否声明变量取决于属性值的时候,才需要使用TagExtraInfo子类,其他的情况使用tld的xml设置即可。
—其他类
TagLibraryInfo,TagInfo,TagAttributeInfo这些主要是给container用的,它们表示tld文件,以及用来取得标签的各种设置。
—使用带属性的标签:
要在标签处理类中添加标签的setter和getter方法,如:
private
int a1;
public void setA1(int value)
{
this.a1 = value;
}
public
int getA1()
{
return
this.a1;
}
然后在tld文件中添加如下节点设置:
<attribute>
<name>a1</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<rtexprvalue>节点表示此属性是否可以使用jsp的表达式,如<my:tag
a1=”${avar}”/>
如果是true,它就会取得范围变量中的avar的值,如果是false,则a1的属性值就是${avar},不会进行jsp的动态处理。