自定义标签库

来源:互联网 发布:数据中立性 编辑:程序博客网 时间:2024/06/05 22:47

自定义标记的创建步骤
1.标记处理类的创建
2.自定义标记库的创建
3.自定义标记库的配置
4.自定义标记的使用

 

正文标签 —— 即对在开始和结束标签之间的内容进行操作的, 标签必须实现 BodyTag 接口。
简单标签 —— 不对其正文操作的标签称为 ,简单标签可以实现 Tag 接口

 

简单标签
第 1 步:创建一个实现了 Tag 接口的标签处理程序
Tag 接口定义了标签处理程序和 JSP 页实现类之间的基本协议。它定义了在标签开始和结束时调用的生存周期和方法。 为了编写标签处理程序,必须实现 Tag 接口。

int doStartTag() throws JspException  处理开始标签
int doEndTag() throws JspException  处理结束标签
Tag getParent()/void setParent(Tag t)  获得/设置标签的父标签
void setPageContext(PageContext pc)  pageContext 属性的 setter 方法
void release()  释放获得的所有资源


TagSupport
通过继承 TagSupport 来实现 Tag 接口
在默认情况下,TagSupport 实现了 doStartTag() 以使它返回 SKIP_BODY 常量,表示将不对标签正文进行判断。 此外,在默认情况下,doEndTag() 方法返回 EVAL_PAGE它表示 JSP 运行时引擎应当对页面的其余部分进行判断。
最后TagSupport 实现了 release(),它设置 pageContext 及其父元素为 null。
TagSupport 类还实现了 IterationTag 接口和 doAfterBody(),这样它就返回 SKIP_BODY。

 

第 2 步:创建一个 TLD 文件
TLD 文件通常保存在 Web 应用程序的 WEB-INF 目录,并在 web.xml 文件中声明。它们一般用 .tld 扩展名结束。
TLD 文件的根元素是 taglib。taglib 描述了一个 标签库 —— 即一组标签/标签处理程序对。

tlib-version 元素对应于标签库版本。
jsp-version 对应于标签库所依赖的 JSP 技术的版本。
short-name 元素定义了 IDE 和其他开发工具可以使用的标签库的简单名。
taglib 元素包含许多 tag 元素,标签库中每一个标签有一个 tag 元素

tag 元素用于将自定义标签映射到它们的自定义标签处理程序。
<name>
<tag-class>
<body-content>


第 3 步:在标签处理程序 Java 类中创建属性


第 4 步:在 TLD 文件中定义属性
<attribute>
name 元素指定属性的名字。
required 元素指定属性是否是必需的(默认值是 false)。
rtexprvalue 属性值是否可以为JSP表达式(类似于<%=…% >的表达式)。


第 5 步:实现 doStartTag() 方法
标签开始时调用 doStartTag() 方法
如果 doStartTag() 返回 SKIP_BODY,那么将不处理标签正文。
如果它返回一个 EVAL_BODY_INCLUDE,那么将处理正文。

 

<%@taglib prefix="my" uri="/mytag"%>
"/mytag"对应于web.xml文件中的<taglib-uri>/mytag</taglib-uri>
<my:hello/>
"my"对应于taglib指令的prefix属性值,"hello"对应于TLD文件中<tag>标记的<name>hello</name>

带属性的标记定义
标记处理类中的属性声明
标记库描述文件中的属性描述

带标记体内容的标记定义
BodyTagSuppport类
doAfterBody()方法
BodyContent类
getString()方法,标记处理类中获取标记体内容对象


简单标记定义
SimpleTagSupport类
doTag()方法
getJspContext()方法
getJspBody()方法
JspFragment类
invoke()方法


<tag>
     <name>max</name>
     <tag-class>tag.MaxTag</tag-class>
     <body-content>empty</body-content>

     <attribute>
         <name>num1</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
     </attribute>

     <attribute>
         <name>num2</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
     </attribute>
 </tag>


<taglib>
       <taglib-uri>/mytag</taglib-uri>
       <taglib-location>/WEB-INF/tlds/MyTaglib.tld</taglib-location>
</taglib>


如果在JSP的taglib指令中直接指定TLD文件的位置,不需要在web.xml中声明标记库的位置
<
%@taglib uri="/WEB-INF/tlds/MyTaglib.tld" prefix="my"%>

 

带标记体内容的自定义标记
BodyTagSupport类-提供处理标记体的方法
常用方法:
BodyContent getBodyContent()-获取标记体对象
BodyContent类-标记体内容的输出流类,扩展于JSPWriter类
常用方法:
String getString()-以字符串形式返回标记体
JspWriter getEnclosingWriter()-获取对标记体进行写操作的流对象


创建标记处理类
要处理标记体,必须实现BodyTag接口,BodyTagSupport实现了该接口
在TLD文件中添加<tag>标记
使用自定义标记
要处理标记体,则doStartTag() 方法的返回值必须是EVAL_BODY_BUFFERED
要重复处理标记体,还需要改写doAfterBody()方法,该方法的返回值必须是EVAL_BODY_AGAIN,否则返回SKIP_BODY
doEndTag()方法中对标记体进行处理

public class GreetTag extends BodyTagSupport {
    private int count=0;
    public int doStartTag() throws JspException {
        return EVAL_BODY_BUFFERED;
    }
    public int doAfterBody() throws JspException{
        if(count<1){
            count++;
            return this.EVAL_BODY_AGAIN;
        }
        return this.SKIP_BODY;
    }
    public int doEndTag() throws JspException {
        try {
            this.pageContext.getOut().write(this.bodyContent.getString());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return EVAL_PAGE;
    }
}


<tag>
    <name>greet</name>
    <tag-class>tag.GreetTag</tag-class>
    <body-content>JSP</body-content>
</tag>


简单标记类定义的一般步骤
从SimpleTagSupport类继承
改写doTag()方法,处理开始和结束标记
使用getJspContext()方法获取Jsp页面上下文对象
调用该对象的getOut()方法可以获取输出流对象
使用getJspBody()方法获取JspFragment对象
调用该对象的invoke()方法输出标记体内容
public class MySimpleTag extends SimpleTagSupport {
    private String name;
    public void setName(String name){
        this.name=name;
    }
    public void doTag() throws JspException {
        try {
            this.getJspContext().getOut().write(name+",");
            JspFragment f=getJspBody();
            if (f != null) f.invoke(null);

        } catch (IOException ex) {           
        }
    }
}
<tag>
      <name>welcome</name>
      <tag-class>tag.MySimpleTag</tag-class>
      <body-content>tagdependent</body-content>
      <attribute>
          <name>name</name>
          <required>true</required>
          <rtexprvalue>true</rtexprvalue>
      </attribute>
  </tag>
<body-content>tagdependent</body-content>表示标记体内容由标记自己去处理,不支持JSP脚本元素的使用


开发带标签体的标签

要开发带标签体的标签,可实现BodyTag接口,也可从BodyTag接口的实现类BodyTagSupport继承,为简化开发,推荐从BodyTagSupport类继承开发。

编写标签对应的实现类时,需要重载BodyTagSupport类几个方法:
doStartTag(), setBodyContent(), doInitBody(), doAfterBody(), doEndTag(),
他们执行顺序:
doStartTag()→doInitBody()→setBodyContent()→doAfterBody()→doEndTag()
doStartTag()方法可返回EVAL_BODY_INCLUDE或SKIP_BODY,
如果返回EVAL_BODY_ INCLUDE则继续执行;
如果返回SKIP_BODY则接下来的doInitBody(),setBodyContent(), 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页面的后续内容将不再执行。

 


开发带标签体的标签
BodyTag.java

package body;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class bodyTag extends BodyTagSupport{

 private int countNum=0;//循环显示时间的次数
 private int currentNum=1;//当前执行次数
 public int getCountNum() {
  return countNum;
 }
 public void setCountNum(int countNum) {
  this.countNum = countNum;
  this.currentNum=1;
 }
//----标签开始时调用此方法-------
 public int doStartTag(){
  try{
   JspWriter out=pageContext.getOut();
   out.print("标签开始了:");
   if(countNum>0)
    return EVAL_BODY_TAG;
   else
    return SKIP_BODY;
  }catch(Exception e){
   System.out.println(e);
   return SKIP_BODY;
  }
 }
//----标签体执行完后调用此方法----
 public int doAfterBody(){
  try{
   JspWriter out=pageContext.getOut();
   out.print("第"+currentNum+"次执行标签体。标签体执行完毕");
   if(countNum>1){//如果还需要执行标签体
    countNum--;
    currentNum++;
    return EVAL_BODY_TAG;
   }else
    return SKIP_BODY;
  }catch(Exception e){
   System.out.println(e);
   return SKIP_BODY;
  }
 }
//----标签结束时调用此方法-------
 public int doEndTag(){
  try{
   JspWriter out=pageContext.getOut();
   //----输出标签体的内容----
   bodyContent.writeOut(bodyContent.getEnclosingWriter());
   out.print("标签结束了。");
  }catch(Exception e){
   System.out.println(e);
  }
  return EVAL_PAGE;
 }
}


开发嵌套的标签
开发迭代的标签

Tag 中定义:
SKIP_BODY隐含0    :不处理标签体,直接调用doEndTag()方法
EVAL_BODY_INCLUDE隐含1:解析标签体,但绕过 doInitBody () 和 setBodyContent () 方法
SKIP_PAGE隐含5  :不解析标签后面的JSP内容
EVAL_PAGE隐含6:解析标签后,继续解析标签后面的JSP内容

IterationTag 中定义:
EVAL_BODY_AGAIN = 2;

BodyTag 中定义:
EVAL_BODY_BUFFERED = 2;    // 将会再一次调用doAferBody方法直到返回SKIP_BODY

在doAferBody中返回SKIP_BODY,表示终止标记正文处理;
若返回的是 EVAL_BODY_BUFFERED ,将会再一次调用doAferBody方法,重新处理标记正文,直到返回SKIP_BODY为止。 


Tag 方法
 可返回的静态常量
 
doStartTag
 SKIP_BODY 不处理标签体,直接调用doEndTag()方法
 EVAL_BODY_INCLUDE解析标签体,但绕过 doInitBody () 和 setBodyContent () 方法
 EVAL_BODY_AGAIN
 EVAL_BODY_BUFFERED 将会再一次调用doAferBody方法
 
doInitBody
 做标签一些初始化工作,无返回值
 
setBodyContent
 在 doInitBody 之后执行,使用setBodyContent得到JSP页面中标签体之间内容
 
doAfterBody
 最终必须返回SKIP_BODY ,否则可能导致OutOfMemoryError,可参考上面①
 
doEndTag
 SKIP_PAGE/EVAL_PAGE
 
public int doAfterBody() throws JspException {

  try {

   this.pageContext.getOut().write("<br>");

  } catch (IOException e) {

      e.printStackTrace();

  }

  if(cou>1){

   cou–;

   return this.EVAL_BODY_AGAIN;

  }else{

   return this.SKIP_BODY;   // 最终必须返回SKIP_BODY

  }

 }

body-content :
tagdependent : 标签体内容 直接被写入BodyContent,由自定义标签类来进行处理,而不被JSP容器解释,

如下:

<test:myList>

select name,age from users

</test:myList>

 

JSP : 接受所有JSP语法,如定制的或内部的tag、scripts、静态HTML、脚本元素、JSP指令和动作。如:

<my:test>

    <%=request.getProtocol()%>      // ②

</my:test>

具体可参考后面附源码。

 

empty : 空标记,即起始标记和结束标记之间没有内容。

下面几种写法都是有效的,

<test:mytag />

<test:mytag uname="Tom" />

<test:mytag></test:mytag>

 

scriptless : 接受文本、EL和JSP动作。如上述②使用<body-content> scriptless </body-content> 则报错,具体可参考后面附源码。

3.     Taglib的部署
 
简单部署
 
方式一
编写tld文件,并且放置到WEB-INF目录或创建子目录,然后在JSP中,使用taglib指令,指定URI即可:
 <%@ taglib prefix=”topxp” uri=”/WEB-INF/mytaglib.tld” %>
这个指令,直接指向一个tld文件。
 
方式二
也可以在web.xml中用taglib-uri和taglib-location标记来定义它的URI和位置的映射,然后在JSP中指定URI的时候,
就直接使用在web.xml中定义的URI即可,这个就不举例子了。
 
打包部署
你也可以将taglib打成jar包,这个时候,你的taglib描述文件(tld文件),必须被放置在jar文件内的META-INF目录(或子目录)下。你可以在这个目录下,放置多个tld文件。
 
如果将taglib打包,这个jar包必须被放置在web-inf/lib目录下。在jar包的meta-inf目录下的tld文件,都可以被自动发现,不需要显式的定义其位置。
 
JSP使用方式一:用URI直接指定jar文件的位置
Jsp中可以使用下面的指令来指定tld文件的位置:
 
<%@ taglib prefix=”topxp” uri=”/WEB-INF/lib/topxptaglib.jar” %>
 
在这种情况下,因为没有直接指定tld文件的位置,所以,tld文件必须遵守一定的命名规则。规则就是:
Tld文件必须命名为:taglib.tld。这样,在meta-inf目录下,只能放置一个tld文件,无法支持多个tld文件。
 
JSP使用方式二:使用定制的URI
如果需要在一个jar包中包含多个tld文件,你必须使用某种方式来区分这些tld文件,这种方式就是:
在tld文件中,用<uri>标记来区别不同的tld文件。如,可以加入下面的<uri>标记进topxptaglib.jar中的tld文件:<uri>
/my_tags</uri>
 
在我们的例子中,假设命名为topxp.tld,下面就是topxp.tld文件的内容,请注意添加了<uri>标记:
 
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"       

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
    <description><![CDATA["系统自定义标签"]]></description>
    <display-name>"Custom Tags"</display-name>
    <tlib-version>1.0</tlib-version>
    <short-name>myTag</short-name>
    <uri>/my_tags</uri>

    <tag>
       <name>out</name>
       <tag-class>org.topxp.taglib.OutTag</tag-class>
       <body-content>empty</body-content>
       <attribute>
           <name>name</name>
           <required>false</required>
           <rtexprvalue>false</rtexprvalue>
       </attribute>

    </tag>

</taglib>