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