jsp tag

1 Tag handler
1.1 重用

   Tag handler被保存在org.apache.jasper.runtime.TagHandlerPool对象池中,以下是TagHandlerPool的几个方法:  
•public TagHandlerPool() 用缺省容量构造TagHandlerPool
•public Tag get(Class handlerClass) throws JspException 从对象池中取出下个可用的tag handler。如果对象池为空,则实例化一个tag handler(需要注意的是,这个新实例化的tag handler并不被对象池管理)。如果tag handler无法被实例化,那么抛出JspException异常。
•public void reuse(Tag handler) 把tag handler放回到对象池中等待重用。如果对象池已经达到了容量上限,那么调用tag handler上的release方法。
•public void release() 在对象池中所有可用的tag handler上调用release方法。

   除了TagHandlerPool之外,还有一个org.apache.jasper.runtime.PerThreadTagHandlerPool对象池。它继承自TagHandlerPool。顾名思义,它是一个基于Thread local的对象池。

   需要注意的是,与继承自TagSupport 和BodyTagSupport 的tag handler不同,实现SimpleTag接口 的tag handler不再被JSP容器缓存。



1.2 TagSupport
     在取得了tag handler之后,JSP容器首先会调用tag hander上的setPageContext(PageContext pc)方法,因此在tag handler的构造函数中不能访问pageContext。之后调用setParent(Tag t)方法和其它属性的注入。
     当遇到标签的起始标记,就会调用public int doStartTag() throws JspException方法。这个方法有Tag.SKIP_BODY和Tag.EVAL_BODY_INCLUDE两个可选的返回值。缺省返回Tag.SKIP_BODY。返回Tag.SKIP_BODY意味着标签之间的内容被忽略;返回Tag.EVAL_BODY_INCLUDE意味着标签之间的内容将被执行。
     如果doStartTag()方法返回值不是Tag.SKIP_BODY,而且在Tag Library Descriptor(tld)文件中该tag的bodycontent属性不是empty,那么在标签之间的内容执行完毕后,就会调用public int doAfterBody() throws JspException方法,这个方法有Tag.SKIP_BODY和IterationTag.EVAL_BODY_AGAIN两个可选的返回值。缺省返回Tag.SKIP_BODY。返回IterationTag.EVAL_BODY_AGAIN意味着标签之间的内容将被执行,然后会再次调用doAfterBody()方法;返回Tag.SKIP_BODY意味着标签之间的内容不再被执行。
     当遇到标签的结束标志,就会调用public int doEndTag() throws JspException方法。这个方法有Tag.EVAL_PAGE和Tag.SKIP_PAGE两个可选的返回值。缺省返回Tag.EVAL_PAGE。返回Tag.EVAL_PAGE意味着按照正常的流程继续执行JSP网页;返回Tag.SKIP_PAGE意味着JSP网页上未处理的部分均被忽略。

1.3 BodyTagSupport
     BodyTagSupport继承了TagSupport,并且实现了BodyTag接口。以下是在BodyTag中声明的两个方法:



Java代码 
1.void setBodyContent(BodyContent b); 
2.void doInitBody() throws JspException; 

   BodyTagSupport可以用于访问标签体。与TagSupport的doStartTag()方法不同的是,BodyTagSupport的doStartTag()方法多了一个可选的返回值BodyTag.EVAL_BODY_BUFFERED。如果doStartTag()方法返回值是BodyTag.EVAL_BODY_BUFFERED,而且在Tag Library Descriptor(tld)文件中该tag的bodycontent属性不是empty,那么JSP容器会首先调用setBodyContent()和doInitBody()方法。然后在标签之间的内容执行完毕后,会接着调用public int doAfterBody() throws JspException方法。需要注意的是,JSP容器在调用setBodyContent()方法之前,首先会调用pageContext. pushBody()方法,在doAfterBody()方法返回了Tag.SKIP_BODY之后,JSP容器会调用pageContext.popBody()方法。以下是类中这几个方法的代码:



Java代码 
1.public BodyContent pushBody() { 
2.    return (BodyContent) pushBody(null); 
3.} 
4. 
5.public JspWriter pushBody(Writer writer) { 
6.    depth++; 
7.    if (depth >= outs.length) { 
8.        BodyContentImpl[] newOuts = new BodyContentImpl[depth + 1]; 
9.        for (int i=0; i<outs.length; i++) { 
10.            newOuts[i] = outs[i]; 
11.        } 
12.        newOuts[depth] = new BodyContentImpl(out); 
13.        outs = newOuts; 
14.    } 
15. 
16.    outs[depth].setWriter(writer); 
17.    out = outs[depth]; 
18. 
19.    // Update the value of the "out" attribute in the page scope 
20.    // attribute namespace of this PageContext 
21.    setAttribute(OUT, out); 
22. 
23.    return outs[depth]; 
24.} 
25. 
26.public JspWriter popBody() { 
27.    depth--; 
28.    if (depth >= 0) { 
29.        out = outs[depth]; 
30.    } else { 
31.        out = baseOut; 
32.    } 
33. 
34.    // Update the value of the "out" attribute in the page scope 
35.    // attribute namespace of this PageContext 
36.    setAttribute(OUT, out); 
37. 
38.    return out; 
39.} 

    从以上代码可以知道,在调用了pageContext.pushBody()方法之后,out上的输出被重定向到BodyContent上了(BodyContent这个抽象类继承了JspWriter ),也就是说标签体的执行结果也被输出到BodyContent上了,这就是在doAfterBody()方法内可以通过getBodyContent().getString()的到标签体输出的原因。



1.4 Simple Tag
     为了简化Tag的开发,从JSP2.0起引入了SimpleTag接口和SimpleTagSupport类。SimpleTag直接继承了JspTag,其定义如下:



Java代码 
1.public void doTag() throws  JspException, IOException; 
2.public void setParent( JspTag parent ); 
3.public JspTag getParent(); 
4.public void setJspContext( JspContext pc ); 
5.public void setJspBody( JspFragment jspBody ); 

   为了访问标签体,SimpleTag中包含了setJspBody( JspFragment jspBody )方法,JSP容器会把标签体封装到JspFragment 后进行注入。在doTag()方法内,可以任意多次的调用getJspBody().invoke(Writer w)方法来执行标签体。在tld文件中,SimpleTag对应的body-content不可以设置成JSP。

   JSP2.0同时新增了DynamicAttributes接口为标签增加动态属性的功能。JSP容器会在调用doTag()方法前,调用其接口上的如下方法进行属性的注入:



Java代码 
1.void setDynamicAttribute(String uri, String localName, Object value) throws JspException 



1.5 Tag File
     Tag File也是JSP2.0新增的主要功能之一,它允许开发人员使用JSP语法来制作标签。Tag File的扩展名为.tag或.tagx。假如Tag File 包含其他完整或片断的Tag File,建议扩展名为.tagf。Tag File通常被放置在WEB-INF/tags/目录下。Tag File的隐含对象(Implicit Object)有request,response,jspContext,session,application,out,config。Tag File中可以使用<jsp:doBody>和<jsp:invoke>这两个action元素。<jsp:doBody>可以用来执行标签体。<jsp:invoke>可以用来执行标签中JspFragment类型的属性。
     Tag File转译后的Java类继承自SimpleTagSupport,需要注意的是,在这个类的setJspContext( JspContext pc )方法中,传入的参数JspContext被org.apache.jasper.runtime.JspContextWrapper进行了包装。被包装的JspContext 上的跟Nested Variable同名的属性也会被保存到JspContextWrapper上。JspContextWrapper的setAttribute()方法会拦截对PAGE_SCOPE属性的设置:这些属性并不保存在被包装的JspContext上,而是保存在JspContextWrapper上。在执行标签内容之前,JSP容器会把JspContextWrapper上Nested 和 AT_BEGIN Variables拷贝到被包装的JspContext 的PAGE_SCOPE中。在执行完标签体之后,JSP容器会复原之前保存在JspContextWrapper上的Nested Variable到被包装的JspContext 上,同时拷贝AT_END Variables到被包装的JspContext 上。

猜你喜欢

转载自fjg0427.iteye.com/blog/1700308