public int doEndTag () throws JspException
This method will be called after returning from doStartTag. The body of the action may or may not have been evaluated, depending on the return value of doStartTag.
If this method returns EVAL_PAGE, the rest of the page continues to be evaluated. If this method returns SKIP_PAGE, the rest of the page is not evaluated, the request is completed, and the doEndTag() methods of enclosing tags are not invoked. If this request was forwarded or included from another page (or Servlet), only the current page evaluation is stopped.
The JSP container will resynchronize the values of any AT_BEGIN and AT_END variables (defined by the associated TagExtraInfo or TLD) after the invocation of doEndTag().
JspException
- if an error occurred while processing this tag
自定义标签必须实现下面三个接口中的一个: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,这样会失去通用性