首先我们需要大致了解开发自定义标签所涉及到的接口与类的层次结构(其中SimpleTag接口与SimpleTagSupport类是JSP2.0中新引入的)。
(一) JSP自定义标签的定义步骤:
1 创建标记处理类
2 创建TLD文件
3 在jsp页面通
过指令引入标签库
1、处理标签的类必须扩展javax.servlet.jsp.TagSupport 或 BodyTagSupport。先来讨论TagSupport
2、TagSupport类的主要属性:
A.parent属性:代表嵌套了当前标签的上层标签的处理类;
B.pageContex属性:代表Web应用中的javax.servlet.jsp.PageContext对象;
3、JSP容器在调用doStartTag或者doEndTag方法前,会先调用setPageContext和setParent方法,设置pageContext和parent。因此在标签处理类中可以直接访问pageContext变量;
4、在TagSupport的构造方法中不能访问pageContext成员变量,因为此时JSP容器还没有调用setPageContext方法对pageContext进行初始化。
先写一个扩展javax.servlet.jsp.TagSupport的标签类MyTag :
我们以Tomcat 6.0.20服务器为例,来看看最简单的myTag.jsp是怎么运行的。
我们仅以myTag.jsp中的
先附上我执行的结果:
this is first field : {这是我第一个属性}
this is first date :2013-03-10 01-28-02
中间体执行吧。 1 :我想知道这里doafterbody都做了些什么了呢
中间体执行吧。 2 :我想知道这里doafterbody都做了些什么了呢
中间体执行吧。 3 :我想知道这里doafterbody都做了些什么了呢
中间体执行吧。 4 :我想知道这里doafterbody都做了些什么了呢
最后的结束标签了,doendtag
让我们来看看Tomcat都做了什么。转到Tomcat的\work\Standalone\localhost\_目录下,可以找到如下的myTag_jsp.java,这个文件就是Tomcat解析myTag.jsp时生成的源文件:
生成的myTag_jsp.java继承于org.apache. jasper.runtime.HttpJspBase。研究这个文件为我们了解定制标签的运行机理提供了途径。
从上面可以看出,Tomcat在解析一个JSP页面时,首先为每一个定制标签定义并实例化了一个TagHandlerPool对象。页面的处理方法覆盖 父类的_ jspService()方法,_jspService方法首先初始化环境,为内置对象赋值。由于myTag.jsp页面整体由一 个
在_jspx_meth_html_html_0()方法中,首先从_jspx_tagPool_html_html_locale池中得到一个 org.apache.struts.taglib.html.HtmlTag的实例,然后设置这个tag实例的页面上下文及上级标签,由于 html:html标签是页面的最顶层标签,所以它的parent是null。然后对该标签的内容进行解析。HTML代码直接输出,下面主要看 看
对
在myTag.jsp中定义了一个
标签类对象实例的池化
为了提高运行效率,Tomcat对所有的定制标签类进行了池化,池化工作由org.apache.jasper. runtime.TagHandlerPool类完成。TagHandlerPool类主要有两个方法,代码如下:
TagHandlerPool.java
public class TagHandlerPool {
private static final int MAX_POOL_SIZE = 5;
private Tag[] handlers;
public synchronized Tag get(Class handlerClass) throws JspException {……}
public synchronized void reuse(Tag handler) {……}
}
TagHandlerPool简单地实现了对标签类的池化,其中MAX_POOL_SIZE是池的初始大小,handlers是一个Tag的数组,存储标 签类的实例。get(Class handlerClass)得到一个指定标签类的实例,如果池中没有可用实例,则新实例化一个。reuse(Tag handler)把handler对象放回池中。
至此,我们对JSP在容器中的运行过程已经了然于胸 了。虽然每种JSP容器的解析结果会有差异,但其中的原理都雷同。对于编写JSP应用,我们并不需要干涉容器中的运行过程,但如果你对整个底层的运行机制 比较熟悉,就能对JSP/Servlet技术有更深的认识。
二 我们再来讨论标签类继承BodyTagSupport
我们仅以myBodyTag.jsp中的
先附上执行后的结果:
---dostartTag---
---setBodyContent---
---doinitBody---
0 :---doAfterBody---
1 :---doAfterBody---
2 :---doAfterBody---
3 :---doAfterBody---
4 :---doAfterBody---
---doEndTag---
中间体执行吧。
中间体执行吧。
中间体执行吧。
中间体执行吧。
中间体执行吧。
中间体执行
让我们来看看Tomcat都做了什么。转到Tomcat的\work\Standalone\localhost\_目录下,可以找到如下的myBodyTag_jsp.java,这个文件就是Tomcat解析myBodyTag.jsp时生成的源文件:
最后总结一下BodyTagSupport执行流程:
他们执行顺序如下:
doStartTag()—>doInitBody()-->setBodyContent()-->doAfterBody()-->doEndTag()
doStartTag()方法可返回EVAL_BODY_INCLUDE或SKIP_BODY,还有EVAL_BODY_BUFFERED;
如果返回EVAL_BODY_INCLUDE则继续执行; 如果返回EVAL_BODY_BUFFERED; 注意了此时的out 引用已经被改赋值为BodyContent对象了。它会把它会通过out.print()设置标签体的内容缓存起来,
直到super.bodyContent.writeOut(getPreviousOut())执行到再输出标签体的内容
如果返回SKIP_BODY则接下来的setBodyContent(), doInitBody(),doAfterBody()三个方法不会被执行,
而直接执行doEndTag()方法。
setBodyContent()方法用于设置标签体内容,如果在此之前要作一些初始化工作,则在doInitBody()方法中完成。
标签体内容执行完后,会调用doAfterBody()方法,此方法可返回EVAL_BODY_TAG, SKIP_BODY,
EVAL_PAGE或SKIP_PAGE。
如果返回EVAL_BODY_TAG则会再次设置标签体内容,直到返回SKIP_BODY;
如果返回EVAL_PAGE则标签体执行完后会继续执行JSP页面中接下来的部分;
如果返回SKIP_PAGE,则JSP页面的后续内容将不再执行。
附上MyBodyTag类:
附上myTag.TLD: