本文自定义输出helloworld标签和自定义list循环遍历标签
自定义标签的基本步骤如下:
(1)把标签处理类及相关类的class文件存放在WEB-INF/classes目录下。
(2)把TLD标签库描述文件存放在WEB-INF目录或者其自定义的子目录下。
(3)在web.xml文件中声明所引用的标签库。
(4)在JSP文件中使用标签库中的标签。




自定义实现Tag接口的标签步骤
一、创建处理标签的java类,标签处理类要实现javax.servlet.jsp.tagext.Tag接口
输出helloworld标签的形式<h:hello></h:hello>
(1)Tag接口中的方法简介
1.public void setPageContext(PageContext pc)由Servlet容器调用传递给处理类pageContext对象
2.public void setParent(Tag t)由Servlet容器调用传递给标签处理类该标签的父标签
3.public Tag getParent()返回该标签的父标签
4.doStartTag()当Servlet容器遇到该标签的启始标记的时候会调用该方法。
doStartTag()方法返回一个整数值,用来决定程序的后续流程。
它有两个可选值,即Tag.SKIP_BODY和Tag.EVAL_BODY_INCLUDE。
Tag.SKIP_BODY表示标签之间的主体内容被忽略,
Tag.EVAL_BODY_INCLUDE表示标签之间的主体内容被正常执行。
5.doEndTag()当Servlet容器遇到标签的结束标志时,就会调用doEndTag()方法。
doEndTag()方法也返回一个整数值,用来决定程序后续流程。
它有两个可选值,即Tag.SKIP_PAGE和Tag.EVAL_PAGE。
Tag.SKIP_PAGE表示立刻停止执行标签后面的JSP代码,网页上未处理的静态内容和Java程序片段均被忽略,任何已有的输出内容立刻返回到客户的浏览器上。
Tag.EVAL_PAGE表示按正常的流程继续执行JSP文件。
6.public void release()当Servlet容器需要释放Tag对象占用的资源时,会调用此方法。

(2)Servlet容器调用Tag对象的相关方法的流程
1.Servlet容器调用Tag对象的setPageContext()和setParent()方法,把当前JSP页面的PageContext对象及父标签处理对象传给当前Tag对象。如果不存在父标签,则把父标签处理对象设为null。
2.Servlet容器调用Tag对象的一系列set方法,设置Tag对象的属性。如果标签没有属性,则无需这个步骤。
3.Servlet容器调用Tag对象的doStartTag()方法。
4.如果doStartTag()方法返回Tag.SKIP_BODY,就不执行标签主体的内容;如果doStartTag()方法返回Tag.EVAL_BODY_INCLUDE,就执行标签主体的内容。
5.Servlet容器调用Tag对象的doEndTag()方法。
6.如果doEndTag()方法返回Tag.SKIP_PAGE,就不执行标签后续的JSP代码;如果doEndTag()方法返回Tag.EVAL_PAGE,就执行标签后续的JSP代码。
注意:一个Tag对象在被创建后,就会一直存在,可以被Servlet容器重复调用。当Web应用终止时,Serlvet容器会先调用该Web应用中的所有Tag对象的release()方法,然后销毁这些Tag对象。

(3)HelloWorld标签处理类cn.pdsu.HelloTaglib

package cn.pdsu;


import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;


/**
 * 类说明:自定义helloword标签
 * 
 * @author 作者: LiuJunGuang
 * @version 创建时间:2011-11-18 下午01:41:09
 */
public class HelloTaglib implements Tag {
	private PageContext pc;


	public void setPageContext(PageContext pc) {
		System.out.println("设置PageContext对象!");
		this.pc = pc;
	}


	public void setParent(Tag t) {
		System.out.println("设置标签的父标签!");
	}


	public Tag getParent() {
		System.out.println("得到标签的父标签!");
		return null;
	}


	public int doStartTag() throws JspException {
		System.out.println("标签开始处理..");
		// 输出helloworld
		try {
			pc.getOut().println("helloworld");
		} catch (IOException e) {
			e.printStackTrace();
		}
		return EVAL_BODY_INCLUDE;// 处理标签中的内容
	}


	public int doEndTag() throws JspException {
		System.out.println("标签结束处理..");
		return EVAL_PAGE;// 处理标签之后的jsp内容
	}


	public void release() {
		System.out.println("释放标签资源");
	}


}




(4)HelloWorld标签描述TLD文件如下:


<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    
  <description>My first taglib!</description>
  <display-name>MyTaglib</display-name>
  <tlib-version>1.0</tlib-version>
  <short-name>h</short-name>
  <uri>http://www.mytaglib.com/taglib</uri>
  <tag>
    <name>hello</name>
    <tag-class>cn.pdsu.HelloTaglib</tag-class>
    <body-content>JSP</body-content>
  </tag>
 </taglib>




 



 

(5)在web.xml中注册标签描述tld文件


 

添加如下内容:


 

<jsp-config>
  	<taglib>
  		<taglib-uri>http://www.mytaglib.com/taglib</taglib-uri>
  		<taglib-location>/WEB-INF/hello.tld</taglib-location>
  	</taglib>
  </jsp-config>






 

(6)在jsp页面中使用标签


<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.mytaglib.com/taglib" prefix="h" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My first taglib!</title>
  </head>
  <body>
  <h:hello></h:hello>
  </body>
</html>




(7)运行结果


设置PageContext对象!


设置标签的父标签!


标签开始处理..


标签结束处理..




二、自定义实现IterationTag接口的标签步骤


(1)IterationTag接口简介


IterationTag接口继承自Tag接口,IterationTag接口增加了重复执行标签主体内容的功能。


IterationTag接口定义了一个doAfterBody()方法,Servlet容器在执行完标签主体内容后,会调用此方法。


如果Serlvet容器未执行标签主体内容,那么就不会调用此方法。


doAfterBody()方法也返回一个整数值,用来决定程序后续流程,它有两个可选值:Tag.SKIP_BODY和IterationTag.EVAL_BODY_AGAIN。


Tag.SKIP_BODY表示不再执行标签主体内容;


IterationTag.EVAL_BODY_AGAIN表示重复执行标签主体内容。


(2)Servlet容器执行IterationTag接口的流程


1.Servlet容器调用IterationTag对象的setPageContext()和setParent()方法,把当前JSP页面的PageContext对象及父标签处理对象传给当前IterationTag对象。如果不存在父标签,则把父标签处理对象设为null。


2.Servlet容器调用IterationTag对象的一系列set方法,设置Tag对象的属性。如果标签没有属性,则无需这个步骤。


3.Servlet容器调用IterationTag对象的doStartTag()方法。


4.如果doStartTag()方法返回Tag.SKIP_BODY,就不执行标签主体的内容;如果doStartTag()方法返回Tag.EVAL_BODY_INCLUDE,就执行标签主体的内容。


5.如果在步骤(4)中Servlet容器执行了标签主体的内容,那么就调用doAfterBody()方法。


6.如果doAfterBody()方法返回Tag.SKIP_BODY,就不再执行标签主体的内容;如果doAfterBody()方法返回IterationTag.EVAL_BODY_AGAIN,就继续重复执行标签主体的内容。


7.Servlet容器调用IterationTag对象的doEndTag()方法。


8.如果doEndTag()方法返回Tag.SKIP_PAGE,就不执行标签后续的JSP代码;如果doEndTag()方法返回Tag.EVAL_PAGE,就执行标签后续的JSP代码。


(3)实例代码见ForTagLib类




三、自定义实现BodyTag接口的标签


(1)BodyTag接口简介


BodyTag接口继承自IterationTag接口,增加了直接访问和操纵标签主体内容的功能。BodyTag接口定义了两个方法。



setBodyContent(BodyContent bc):Servlet容器通过此方法向BodyTag对象传递一个用于缓存标签主体的执行结果的BodyConent对象。



doInitBody():当Servlet容器调用完setBodyContent()方法之后,在第一次执行标签主体之前,先调用此方法,该方法用于为执行标签主体做初始化工作。


(2)只要符合以下两种条件之一,setBodyContent(BodyContent bc)和doInitBody()就不会被Servlet容器调用:



1.标签主体为空。



2.doStartTag()方法的返回值为Tag.SKIP_BODY或者Tag.EVAL_BODY_INCLUDE。


只有同时符合以下两个条件,Servlet容器才会调用setBodyContent(BodyContent bc)和doInitBody()方法:



1.标签主体不为空。



2.doStartTag()方法的返回值为BodyTag.EVAL_BODY_BUFFERED。


以上提到的BodyTag.EVAL_BODY_BUFFERED是在BodyTag接口中定义的int类型的静态常量,它可作为doStartTag()方法的返回值,指示Servlet容器调用BodyTag对象的setBodyContent()和doInitBody()方法。


(3)Servlet容器调用BodyTag对象的流程


1.Servlet容器调用BodyTag对象的setPageContext()和setParent()方法,把当前JSP页面的PageContext对象及父标签处理对象传给当前BodyTag对象。如果不存在父标签,则把父标签处理对象设为null。


2.Servlet容器调用BodyTag对象的一系列set方法,设置Tag对象的属性。如果标签没有属性,则无需这个步骤。


3.Servlet容器调用BodyTag对象的doStartTag()方法。


4.如果doStartTag()方法返回Tag.SKIP_BODY,就不执行标签主体的内容;如果doStartTag()方法返回Tag.EVAL_BODY_INCLUDE,就执行标签主体的内容;如果doStartTag()方法返回BodyTag.EVAL_BODY_BUFFERED,就先调用setBodyContent()和doInitBody()方法,再执行标签主体的内容。


5.如果在步骤(4)中Servlet容器执行了标签主体的内容,那么就调用doAfterBody()方法。


6.如果doAfterBody()方法返回Tag.SKIP_BODY,就不再执行标签主体的内容;如果doAfterBody()方法返回IterationTag.EVAL_BODY_AGAIN,就继续重复执行标签主体的内容。


7.Servlet容器调用BodyTag对象的doEndTag()方法。


8.如果doEndTag()方法返回Tag.SKIP_PAGE,就不执行标签后续的JSP代码;如果doEndTag()方法返回Tag.EVAL_PAGE,就执行标签后续的JSP代码。




四、TagSupport类和BodyTagSupport类


TagSupport类和BodyTagSupport类是标签实现类,其中TagSupport类实现了IterationTag接口,而BodyTagSupport类则继承自TagSupport类,并且实现了BodyTag接口。


用户自定义的标签处理类可以继承TagSupport类或者BodyTagSupport类。   




五、创建标签库描述文件



标签库描述文件(Tag Library Descriptor,TLD),采用XML文件格式,对标签库及库中的标签做了描述。TLD文件中的元素可以分为3类:



1.<taglib>:标签库元素。



2.<tag>:标签元素。



3.<attribute>:标签属性元素。






1、标签库元素<taglib>



<taglib>元素的子元素



tlib-version 指定标签库的版本


jsp-version

指定JSP的版本


short-name

指定标签库默认的前缀名(prefix)


uri

设定标签库的唯一访问标识符


info

设定标签库的说明信息


2、标签元素<tag>


name

设定标签的名字


tag-class

设定Tag的处理类


body-content

设定标签主体的类型


info

设定标签的说明信息




empty:标签主体为空。



scriptless:标签主体不为空,并且包含JSP的EL表达式和动作元素,但不能包含JSP的脚本元素。所谓动作元素是指<jsp:include>和<jsp:forward>等以“jsp”为前缀的JSP内置标签。所谓脚本元素是指“<%!”和“%>”、“<%”和“%>”和“<%=”和“%>”这3种以“<%”开头的JSP标记。



JSP:标签主体不为空,并且包含JSP代码。在JSP代码中可以包含EL表达式、动作元素和脚本元素。<body-content>子元素的scriptless可选值与jsp可选值的区别在于前者不能包含JSP的脚本元素。



tagdependant:标签主体不为空,并且标签主体内容由标签处理类来解析和处理。标签主体的所有代码都会原封不动的传给标签处理类,而不是把标签主体的执行结果传给标签处理类。假定用户定义了一个<sql:query>标签,它的<body-content>元素的值为tagdependant。


3、标签属性元素<attribute>


name

属性名称


required

属性是否是必须的,默认为false



rtexprvalue 属性值是否可以为基于“<%=”和“%>”形式的Java表达式或者EL表达式