jsp tag

本文基于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中声明的两个方法:
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、付费专栏及课程。

余额充值