`
whitesock
  • 浏览: 479114 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

JSP Tag

    博客分类:
  • EE
阅读更多

本文基于Apache Tomcat v5.0。

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中声明的两个方法:

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()方法。以下是类中这几个方法的代码:

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,其定义如下:

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()方法前,调用其接口上的如下方法进行属性的注入:

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 上。

8
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics