自定义标签必须实现下面三个接口中的一个:
Tag
、
IterationTag
、
BodyTag
1.Tag
如果要实现这个接口,可以通过扩展
TagSupport
这个类,来写自己需要的方法,而不需要把
Tag
接口中的所有方法实现。
Tag
接口的方法:
doStartTag()
、
doEndTag()
、
getParent()
、
setParent()
、
release()
、
setPageContext()
在
Tag
类代码中不能像
jsp
一样,直接使用
out
隐含对象,他有一个对象可以使用
pageContext
,通过它的
getOut()
方法可以得到
out
对象。在标签内部,访问任何的隐含对象,都是通过调用
pageContext
的
set
方法。
2.IterationTag
IterationTag
接口与
Tag
接口类似,用于当一个自定义标签需要重复计算它的代码体的情况下。它扩展
Tag
接口并实现了一个新的方法
doAfterBody()
来实现循环,这个方法只有从
doStartTag()
返回
EVAL_BODY_INCLUDE
时才被调用。在执行
doAfterBody()
方法时,如果返回的是
EVAL_BODY_AGAIN
,那么将再次执行
doAfterBody()
方法,直到
doAfterBody()
返回的是
SKIP_BODY
或者
EVAL_BODY_INCLUDE
。
3.BodyTag
BodyTag
接口扩展了
IterationTag
并提供了对代码体内容进行操作的功能。就是在计算代码体的时候可以对已经形成的代码体进行修改。
BodyContent
对象就是用来保存对自定义标签体计算的结果。它有一个新方法
doInitBody()
,这个方法只有在
doStartTag()
方法返回
EVAL_BODY_BUFFERED
时才调用,此时它将创建一个
BodyContent
对象保存结果。
扩展自定义标签:
添加属性
首先要在
tld
文件中加入一个属性元素,然后在
java
文件中需要定义这个属性以及它的的
setter
方法。属性
<attribute>
元素有四个子元素分别是
<name>
、
<required>
、
<rtexprvalue>
、
<description>
,这里
<rtexprvalue>
表示的是属性是否接受
scriptlet
表达式的计算结果,默认情况下为
false
,即只能接受静态值。
添加变量
可以在
tld
文件中给自定义标签加入一个
<variable>
元素,它的子元素包括
<name-given>
表示保存变量的名字,
<variable-class>
表示变量的
java
类型,
<declared>
用
boolean
表示这个变量是否为新的,
<scope>
表示变量的使用范围(
AT_BEGIN
表示从起始标签起,
AT_END
表示从终止标签后,
NESTED
表示起始标签和终止标签之间)。定义了变量之后,需要在
java
文件中把这个变量用
pageContext.setAttribute("",object);
这里
key
值应该就是变量对外的名字。
使用
TagExtraInfo
(
TEI
)类
这个对象中有两类对象可以使用,
TagData
(保存标签属性的信息)、
VariableInfo
(描述代码变量)
一段
TagExtraInfo
类代码实例:
public VariableInfo[] getVariableInfo(TagData data) {
String variableName = data.getAttributeString("name");
VariableInfo vi =
new VariableInfo(variableName,"String []", true, VariableInfo.AT_END);
VariableInfo[] tagVariables = new VariableInfo[1];
tagVariables[0] = vi;
return tagVariables;
}
可以通过
TagData
类的
getAttributeString
方法得到某个属性的值,还有另外一个方法
getAttribute
也是得到某个属性的值不过返回的是一个对象。而
getVariableInfo
方法必须返回一个
VariableInfo
数组。除此之外,还需要在
tld
中的元素定义
<tag-class>
后加入一个
<tei-class>
元素,说明
TEI
类的全称。
pageContext
对象中含有的方法包括:
getOut();getPage();getRequest();getResponse();getServletConfig();getServletContext();getSession();
Tag
接口中的返回常数意义:
EVAL_BODY_INCLUDE
:告诉服务器正文的内容,并把这些内容送入输出流
SKIP_BODY
:告诉服务器不要处理正文内容
EVAL_PAGE
:让服务器继续执行页面
SKIP_PAGE
:让服务器不要处理剩余的页面
EVAL_BODY_AGAIN
:让服务器继续处理正文内容,只有
doAfterBody
方法可以返回
EVAL_BODY_BUFFERED
:
BodyTag
接口的字段,在
doStartTag()
返回
EVAL_BODY_INCLUDE
、
SKIP_BODY
一般由
doStartTag()
返回,而
EVAL_PAPGE
、
SKIP_PAGE
由
doEndTag()
返回。
在调用
doStartTag()
方法之前其实标记还调用了其他两个方法:
setPageContext()
和
setParent()
;所以在后面的方法中可以使用
pageContext
和
parent
对象,如果需要的话。
让自定义标签在页面中创建对象时必须使用一个标准的
JSP
对象
TagExtraInfo
类,它可以创建脚本变量还可以在编译的时候对标签进行检验,
TEI
类仅可以生成由
setAttribute
方法存储在
PageContext
对象中的变量,而并不是单独生成变量。
通过
TEI
类定义脚本变量可以让使用者自己定义在页面中使用对象的名称。
除了使用
TEI
类方法之外,还可以简单的在
TLD
中定义一个
<variable>
对象来使用自定义对象,用法如下:
<variable>
<name-from-attribute>name</name-from-attribute>
<variable-class>String []</variable-class>
<declare>true</declare>
<scope>AT_END</scope>
</variable>
对于
variable
的子元素,
<name-from-attribute>
指的是创建的变量名称从属性
name
中来取得,当然也可以通过
<name-given>
元素来限制变量的名称。注意这两个元素是互斥的。
一个扩展
BodyTagSupport
的自定义标记的生命周期如下:
1.
创建标记
2.
调用
Setter
方法
3.
调用
doStartTag()
方法
4.
调用
setBodyContent()
方法
5.
调用
InitBody()
方法
6.
处理标记的
Body
7.doAfterBody()
;根据返回值,如果为
EVAL_BODY_AGAIN
,继续执行
6
,如果不是,执行
8
8.
调用
doEndTag()
方法
9.
判断标记是否需要重用,如果要,执行
4
;否则执行
release()
方法。
TagSupport
类的方法
findAncestorWithClass()
方法可以用来查找指定的父类,它有两个参数一个为本身的类名,还有一个就是要查找的父类的名称,如果没有返回
null
;例如
ParentTag parent = (ParentTag) this.findAncestorWithClass(this,ParentTag.class);
自定义标记的验证方法:
JSP1.1
TEI
类可以在编译时刻检验自己的标记,这个类中有一个
isValid()
方法,如果
TLD
中为这个标记定义了这个
TEI
类,那么网页在编译的时候将会调用这个方法,并且会传入一个包含属性具体内容的参数
TagData
。(在
JSP1.2
中同样有效)
JSP1.2
JSP1.2
中引入一个新的标记检验方法,定义了一个新类
TagLibraryValidator
,并且可以由此派生出检验标志的类,大多数情况下仅使用这个类的
validate()
方法,它有三个参数:
prefix
(在
taglib
指令中定义的前缀);
uri
(
TLD
文件中的
URI
);
page
(
JSP
页的
PageData XML
版本),
validate()
方法返回值为
null
时表示验证成功,否则返回的
String
类型将是一个错误信息。
当
validator
在
TLD
文件中定义时,它应该放在
<tag>
元素定义的外面,因为它是用来处理验证标记库中的所有标记的。
<validator><validator-class></validator-class></validator>
。
比较
JSP1.2
和
JSP1.1
中的方法:
TagLibraryValidator
比
TEI
类更全面,可以用来检测整个网页,而不仅仅是标记本身,可以用来处理标记间的合作,并且这种方法可以用来通知程序员错误出在哪里,但是同时它的方法也比
TEI
类的方法复杂多了,因为它需要遍历整个
XML
版本的
JSP
(完成
getAttributeValue
方法)。
JSP1.2
中的
TryCatchFinally
接口:
这个接口主要是用于当自定义标记出现异常时释放自定义标记中的资源使用的,它定义了两个方法:
public void doCatch(Throwable t);
(当
doStartTag,doInitBody,doAfterBody,doEndTag
方法出现异常时会调用这个方法)
piblic void doFinally();
(当
doEndTag
被调用后,无论是否出现异常都会调用这个方法,就像程序中的
finally
块,可以用来释放资源)
在
JSP1.2
中,可以通过在
tld
文件中加入一个元素
<uri></uri>
来指定自己的在
taglib
指令中使用的名称,然后把这个
tld
文件与
Manifest.mf
一起放在
META-INF
目录中,那么在页面中就可以非常方便地导入这些
tld
。
编写自定义标记的原则:
1.
使用脚本变量(允许设计者为脚本变量起名、将脚本变量的数量减到最小、使用一个组合脚本对象和存取函数即使用
JavaBean
)
2.
当设计相互协作的标记时应该尽量避免创建一套新的语言,应当尽量使用脚本变量
3.
编写代码而不是内容,不要在自定义标记中产生
HTML
,这样会失去通用性