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;  
void setBodyContent(BodyContent b); 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. }  
public BodyContent pushBody() {     return (BodyContent) pushBody(null); }  public JspWriter pushBody(Writer writer) {     depth++;     if (depth >= outs.length) {         BodyContentImpl[] newOuts = new BodyContentImpl[depth + 1];         for (int i=0; i<outs.length; i++) {             newOuts[i] = outs[i];         }         newOuts[depth] = new BodyContentImpl(out);         outs = newOuts;     }      outs[depth].setWriter(writer);     out = outs[depth];      // Update the value of the "out" attribute in the page scope     // attribute namespace of this PageContext     setAttribute(OUT, out);      return outs[depth]; }  public JspWriter popBody() {     depth--;     if (depth >= 0) {         out = outs[depth];     } else {         out = baseOut;     }      // Update the value of the "out" attribute in the page scope     // attribute namespace of this PageContext     setAttribute(OUT, out);      return out; }
    从以上代码可以知道,在调用了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 );  
public void doTag() throws  JspException, IOException; public void setParent( JspTag parent ); public JspTag getParent(); public void setJspContext( JspContext pc ); 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  
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 上。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值