返回值                                意义
SKIP_BODY                         表示不用处理标签体,直接调用doEndTag()方法。
SKIP_PAGE                          忽略标签后面的JSP内容。
EVAL_PAGE                         处理标签后,继续处理JSP后面的内容。 
EVAL_BODY_BUFFERED         表示需要处理标签体。
EVAL_BODY_INCLUDE           表示需要处理标签体,但绕过setBodyContent()和doInitBody()方法
EVAL_BODY_AGAIN              对标签体循环处理。 


标签库Taglib

标签被定义和分布在一个称为标签库的结构中,一个标签库是由元信息和类组成的集合:

1.标签处理器(JAVA):实现定制标签功能的Java类。

2.标签附加信息(TEI):向JSP容器提供编辑以确认标签属性和创建变量的类。

3.标签库描述器(TLD):描述单个标签和整个标签库属性的XML文档。

标签处理器(JAVA)和标签附加信息(TEI)需要定位在 JSP容器类 载入器 可以找到的地方。标签库描述器(TLD)可在URL指定的符意位置。

一、标签实现

1. 开发步骤

a.定义标签的名字、属性、声明的变量和标签体的内容。

b.编写标签库描述器TLD。

c.编写标签处理器。

d.在JSP页面中使用标签。


2. JSP页面在JSP容器中的转换步骤:

JSP页面存在三种形式:

jsp文件
java文件
class文件

a.指令元素、和向JSP容器提供转换时信息。

b.HTML行在_jspService()方法中依顺序转换到out.print()语名中。

c.脚本元素的声明被原封不动地复制到_jspService()方法外的源码中。

d.脚本元素的表达式在_jspService()方法中依顺序转换到out.print()语名中。

e.脚本元素的Scriptlet被原封不动地复制到_jspService()方法中。

f.行为元素被转换为执行其功能的运行时逻辑代码。

g.定制标签被扩展到调用其相应标签处理器中方法的Java语句中。


3.标签在JSP容器中的转换步骤:

a.JSP容器使用taglib指令元素定位标签库描述器,将页面中用到的定制标签和TLD相匹配。

b.读取标签库描述器的标签列表和每一标签相关的类名字。

c.在页面中遇到一个标签时,查找与具有指定名字的标签前缀相关的一个标签库。

d.容器使用在TLD中找到的标签结构信息生成一系列完成标签功能的Java语句。


二、标签库描述器(TLD)

标签库描述器是一个描述 整个标签库标记信息库中每个标签处理器其属性 的XML文档。

标签库描述器的DTD由一个简单的元素组成,此元素包含下列一些子元素。

1 : 整个标签库标记信息

< tlib - version > 1.0 </ tlib - version >

标签库版本号。是一个点式十进制数,最多为4组小数点分隔的数字组成。

   < jsp - version > 1.2 </ jsp - version >

标签库所需的JSP规范最低版本。例如JSP1.1

   < short - name > wentao </ short - name >

标签库的缩写名。JSP可以使用该名字作为库中标签的缺省前缀。

< uri > http: // www.wentao.com/taglibs/wentao</uri>

标签库唯一URI的元素。典型URL位置来自可下载taglib的位置。

< description > wentao Tage Liberary. </ description >

标签库描述信息。

2 : 每个标签处理器及其属性 (以displaytag-11.tld为例)

     < tag >
        
< name > table </ name >
        
< tagclass > org.displaytag.tags.TableTag </ tagclass >
        
< teiclass > org.displaytag.tags.TableTagExtraInfo </ teiclass >
        
< bodycontent > JSP </ bodycontent >
        
< info > my name is liuwentao </ info >
        
< attribute >
            
< name > list </ name >
            
< required > false </ required >
            
< rtexprvalue > true </ rtexprvalue >
        
</ attribute >
    .

    
</ tag >

tag在TLD中加入标签,描述组成库的每个标签。

         < name > table </ name >

name与标签库的名字前缀一起使用的标签的名字,是JSP容器唯一的标签标识 <wentao:table/>。

< tagclass > org.displaytag.tags.TableTag </ tagclass >

tagclass实现标签的标签处理器类的全名。

< teiclass > org.displaytag.tags.TableTagExtraInfo </ teiclass >

teiclass标签附加信息(TEI)类的全名。TEI类给出关于标签处理器创建变量及对标签属性性执行的任意有效性验证的信息。

         < bodycontent > JSP </ bodycontent >

bodycontent描述标签处理器如何使用标签体的内容。有三种取值:

empty:表示标签体必须为空;
JSP:表示脚本元素和模板及其它标签一样被评估。
tagdependent:体内容被原封不动写入BodyContent,其它脚本元素以源码形式出现,而不被JSP容器解释。

 

         < info > my name is liuwentao </ info >

info标签的人工可读描述性信息。

     <attribute>
            <name>list</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>

attribute使用标签时被编码的属性信息。用于定义标签的属性。


3 :属性:

             <name>list</name>

属性的名字。

            <required>false</required>

true|false:属性在标签用到的位置是否要被编码。

            <rtexprvalue>true</rtexprvalue>

true|false:属性值能否用表达式指定。


三、标签处理器

标签处理器是通过实现JSP容器调用的一系列预定义方法执行定制标签行为的一个Java类。

标签处理器实现了标签的行为,标签处理器是Java类。

1. 标签处理器的工作方式

a.导入javax.servlet.jsp和javax.servlet.jsp.tagext包。

b.实现javax.servlet.jsp.tagext包中的Tag接口或BodyTag接口。BodyTag是Tag的子接口。

c.继承TagSupport类或BodyTagSuppoert类。它们是上述接口的缺省实现。

d.重载public int doStartTag() throws JspException方法。


2.标签处理器的接口与实现

javax.servlet.jsp.tagext.Tag是实现标签的最基本的接口。

javax.servlet.jsp.tagext.TagSupport是实现Tag接口的具体类。

通常情况下继承tagSupport类而不直接实现Tag接口通常是有益的。除了对所有必需方法提供了缺省实现外、还保存了pageContext对象及对嵌套标签的支持。

Tag接口包含4个常量 ,表示doStartTag()和doEndTag()方法可能的返回码。

         // 表示需要处理标签体。 
        int EVAL_BODY_INCLUDE = 1;

EVAL_BODY_INCLUDE 当doStartTag()返回时,指明servlet应对 标签体 进行评估。

     // 处理标签后,继续处理JSP后面的内容。
    int EVAL_PAGE = 6;

EVAL_PAGE当doEndTag()返回时,指明页面其余部分应被评估。


     //表示不用处理标签体,直接调用doEndTag()方法。
    int SKIP_BODY = 0;

SKIP_BODY当doStartTag()返回时,指明servlet应忽视标签体。

     //忽略标签后面的JSP内容。
    int SKIP_PAGE = 5;

SKIP_PAGE当doEndTag()返回时,指明页面其余部分就被跳过。


注 :body 这2个是 doStartTag()方法里面调用的。
          page 这2个是  doEndTag()方法里面调用的。


Tag接口的方法

1 :生成的servlet在请求处理器执行其它任务前首先调用此方法

  public   void  setPageContext(javax.servlet.jsp.PageContext pageContext) 

,实现类应保存上下文对象以便它可以在标签生命期中使用,从页面上下文中标签处理器可以访问所有JSP隐含对象。


2 :使用一个标答可以找到操作栈中它上面的标签

public   void  setParent(javax.servlet.jsp.tagext.Tag tag)

。在setPageContext后立即调用

  public  javax.servlet.jsp.tagext.Tag getParent()

返回父标签。

3 :在设置了页面上下文父标签开始标记中编码的属性后调用

     public   int  doStartTag()  throws  javax.servlet.jsp.JspException 

。返回码表明JSP实现servlet是否就评估标签体。


4 :当遇到结否标记时调用

     public   int  doEndTag()  throws  javax.servlet.jsp.JspException

。返回码表明JSP是否就继纽页面的其余部份。


5 :确保在页面退出前被调用

    public void release()

,释放资源并重置标签处理器状态。


TagSupport类的方法


1 :为所需的父标签处理器查找运行时标签栈。一个标签处理器可以提供其范围内子标签调用的方法 :

public static final javax.servlet.jsp.tagext.Tag findAncestorWithClass(javax.servlet.jsp.tagext.Tag tag, java.lang.Class aClass)

2 :保存和检索在id属性中指定的名字 :

    public void setId(java.lang.String string) 

3 :在本地哈希表中设置指定名字的值 :

public void setValue(java.lang.String string, java.lang.Object object)

4 :从本地哈希表中获取指定名称的值 :

public java.lang.Object getValue(java.lang.String string)

5 :从本地哈希表中删除指定名称的值 :

public void removeValue(java.lang.String string)

6 :返回哈希表中关键字的一个枚举 :

public java.util.Enumeration getValues()

 

3. 标签处理器的生命期

   前面的内容  < br >
   
< mytag:helloworld ></ mytag:helloworld >
   后面的内容

为例 :



a.生成servlet需要创建标签处理器类的一个实例。实现方式通常是调用JSP容器工厂类的一个方法,
工厂类 包含一个标签处理器实例池 以使其可重用 不再处于激活状态的对象

b.初始化标签处理器,使servlet获知其存在性。servlet通过调用标签处理器的两个方法实现此过程:

   public void setPageContext(javax.servlet.jsp.PageContext pageContext)

public void setParent(javax.servlet.jsp.tagext.Tag tag)

c.如果标签具有属性,属性的取值通过处理器提供setter方法传入到对象。属性setter方法是一个标签支持属性所需的唯一方法。

d.页面的上下文和父标签已被调置,并已具备属性。此时调用标签处理器的doStartTag()方法,该方法可以读取这些变量并执行实现标答功能所需的计算和操作。doStartTag()方法必须返回一个整型数。返回EVAL_BODY_INCLUDE则正常处理标签体,返回SKIP_BODY则忽略标签体

e.标签体被评估或忽视后调用标签处理器的doEndTag()方法,返回EVAL_PAGE则页面的其余部分被评估,返回SKIP_PAGE则servlet代码立即从_jspService()中返回。




4.体标签处理器的接口与实现

javax.servlet.jsp.tagext.BodyTag是Tag的子接口。

javax.servlet.jsp.tagext.BodyTagSupport是实现BodyTag类。




BodyTagSupport 有一个这样的方法 :

 public void setBodyContent(javax.servlet.jsp.tagext.BodyContent bodyContent) 


BodyContent是javax.servlet.jsp.JspWriter的子类,但与其父类有所区别。

public abstract  class  BodyContent extends javax.servlet.jsp.JspWriter



BodyContent对象的内容不自动写了入servlet的输出流,而是积累在一字符串缓存中。当标签体完成后 其对象 仍可在doEndTag()方法中可以应用,由getString()或getReader()方法操作。并在必要时修改及写入恢复的JspWriter输出流。

BodyContent类的方法

  public void flush() throws java.io.IOException

复写JspWrite.flush()方法以便它总是产生溢出。

public void clearBody()

重置BodyContent缓存为空。

    public abstract java.io.Reader getReader();

返回Reader读取体内容。

    public abstract java.lang.String getString();

返回包含体内容的一个字符串。

    public abstract void writeOut(java.io.Writer writer) throws java.io.IOException;

将体内容写入指定输出。

 public javax.servlet.jsp.JspWriter getEnclosingWriter()

返回栈中下一个更高的写入者对象(可能是另一个BodyContent对象)。


BodyTag接口定义了一个新的整型常量



EVAL_BODY_TAG当doStartTag()返回时,使得新的BodyContent对象被创建并与此标签处理器相关联。当doAfterBody()返回时,使得JSPservlet在修改完此标签控制的任意变量后再次评估体。

BodyTag接口的方法

void setBodyContent(javax.servlet.jsp.tagext.BodyContent bodyContent);

在当前JspWriter已被写入,一个新的BodyContent在被创建后由Jspservlet调用,它发生在doStartTag()之后。

 void doInitBody() throws javax.servlet.jsp.JspException;

setBodyContent()之后,体被评估前调用的生命期方法。如果多次评估体,此方法只调用一次。

 public int doAfterBody() throws javax.servlet.jsp.JspException 

体被评估后,BodyContent写入者仍处于激活状态时调用的生命期方法。此方法必须返回EVAL_BODY_TAG或SKIP_BODY,若返回EVAL_BODY_TAG时体再次被评估。

BodyTagSupport类的方法

  public int doStartTag() throws javax.servlet.jsp.JspException 

复写TagSupport中的doStartTag()方法。

 public int doEndTag() throws javax.servlet.jsp.JspException

调用TagSupport中的doEndTag()方法,返回结果。

public void setBodyContent(javax.servlet.jsp.tagext.BodyContent bodyContent)

在一保护成员变量bodyContent中保存新的体内容对象,子类可直接访问此对象。

public void doInitBody() throws javax.servlet.jsp.JspException 

缺省什么都不做。被需要执行初始化的子类所复写。

public int doAfterBody() throws javax.servlet.jsp.JspException

每次体被评估后由JSPservlet调用,体内容对象仍处于激活状态。返回SKEP_BODY或EVAL_BODY_TAG则体再次被评估

 public void release() 

设置bodyContent对象为null,然后调用super.release()。

public javax.servlet.jsp.tagext.BodyContent getBodyContent()

返回bodyContent变量。子类已经可以访问保护变量,但此方法允许无关的标签处理类对此体内容发送输出。

public javax.servlet.jsp.JspWriter getPreviousOut() 

在bodyContent变量上调用getEnclosingWriter()并返回结果的简便方法。

5.体标签处理器的生命期

a.生成servlet需要创建标签处理器类的一个实例。实现方式通常是调用JSP容器的工厂类的一个方法,工厂类包含一个标签处理器实例池以使其可重用不再处于激活状态的对象。

b.初始化标签处理器,使servlet获知其存在性。servlet通过调用标签处理器的两个方法实现此过程:setPageContext(PageContextctx)和setParent(Tagparent)。

c.如果标签具有属性,属性的取值通过处理器提供setter方法传入到对象。属性setter方法是一个标签支持属性所需的唯一方法。

d.页面的上下文和父标签已被调置,并已具备属性。调用标签处理器的doStartTag()方法,该方法可以读取这些变量并执行实现标答功能所需的计算和操作。

doStartTag()方法必须返回一个整型数。

返回EVAL_BODY_TAG则正常处理标签体(跳到e);

返回SKIP_BODY则从初始JSP页面中直到此标签结束标记处的内容均被忽略。(跳到f)

e.如果返回EVAL_BODY_TAG时,则正常处理标签体。

e1.在栈中保存当前的JspWriter对象,创建新的BodyContent对象,并将其置为JSP页面的out对象保存在上下文范围内名为name的属性中。并调用它的setBodyContent()方法。

e2.调用doInitBody()方法进行初始化。

e3.处理标签体。将输出写入BodyContent对象中,此过程依赖于TLD的标签元素,有三种可能取值。

e4.调用doAfterBody()方法,将体内体内容写入JspWriter,可如下实现:

JspWriterout = bodyContent.getEnclosingWriter();
out.println(bodyContent.getString());
// bodyContent.writeOut(out);
bodyContent.clear();

e5.doAfterBody()方法返回两种可能:

返回EVAL_BODY_TAG时,再对标签体进行评估,这是数组和枚举被循环处理的典型情况。

返回SKIP_PAGE时,继续页面的其余部份。

e6.体内容完成,因此创建它的过程被反向:

调用pageContent.popBody()方法检索前面的JspWriter对象。

将写入者设置回out隐含对象。

f.标签体被评估或忽视后调用doEndTag()方法,允许标签处理器像输出流发回内容。

返回EVAL_PAGE则页面的其余部分被评估;

返回SKIP_PAGE则servlet代码立即从_jspService()中返回。

g.此时体的内容在受保护的bodyContent对象中仍然可用。

可以将它写入servlet输出流中:

JspWriterout=pageContext.getOut();

out.println(bodyContent.getString());

或者

bodyContent.WriteOut(pageContext.getOut());


6.标签附加信息类


四、标签指令

taglib指令元素的目的是指定TLD的位置,设置在页面上与标签区分开来的一个短别名。

语法:

<% @taglib uri = " /helloworld "  prefix = " mytag " %>

属性:prefix:用于标识标签库的唯一标识。uri:标签库本身的URI。

uri不必指向一个实际文件,它是JSP容器可以在web.xml中查找实际文件位置的唯一标识符。