全部博文(2065)
分类: Java
2010-03-07 00:02:47
JSP自定义标签整理专题
[整理人:遥方
整理时间:
一、基本概念
1.
标签(Tag):
标签是一种XML元素,通过标签可以使JSP网页变得简洁并且易于维护,还可以方便地实现同一个JSP文件支持多种语言版本。由于标签是XML元素,所以它的名称和属性都是大小写敏感的。
PS:如果你用PHP生成一段XML文件然后让JSP进行展现一样可以实现的。即同一个JSP页面里面是可以混合不同种语言的!
2.标签库(Tag library):
由一系列功能相似、逻辑上互相联系的标签构成的集合称为标签库。
PS:同一个标签库里面的各种标签其功能是非常类似的。
3.标签库描述文件(Tag Library Descriptor):
标签库描述文件是一个XML文件,这个文件提供了标签库中类和JSP中对标签引用的映射关系。它是一个配置文件,和web.xml是类似的。
PS:注意我们在编写一个新的自定义标签的时候一定要编写好相关的描述文件进来即tld文件。这个文件的作用其实就是提供这种标签库中类与标签之间的映射关系。
可以这样来理解就是一个WEB.xml文件,即自定哪个一个名称与哪个servlets类映射过来
4.标签处理类(Tag Handle Class):
标签处理类是一个Java类,这个类继承了TagSupport或者扩展了SimpleTag接口,通过这个类可以实现自定义JSP标签的具体功能
(PS:即这个标签到底是由谁来处理的相当于是定义了一个响应)
二、
自定义标签的格式
<% @ taglib prefix=”someprefix” uri=”/sometaglib”
%>
为了使到JSP容器能够使用标签库中的自定义行为,必须满足以下两个条件:
1)从一个指定的标签库中识别出代表这种自定义行为的标签(前缀prefix)
2)找到实现这些自定义行为的具体类 (实现的具体类uri)
PS:即这个标签它是合法的,另外还可以找到其具体的实现类来实现这个标签!
如果同一个页面中使用相同前缀的元素都属于一个标签库了。每个标签库都定义了一个默认的前缀。前缀是可以自己随意定义的不过不能是保留字!
uri属性满足了以上的第二个要求。为每个自定义行为找到对应的类。这个uri包含了一个字符串,容器用它来定位TLD文件。在TLD文件中可以找到标签库中所有标签处理类的名称.
(即可以通过我们的uri属性能够快速找到TLD文件而在这个TLD文件里面就可以定义标签处理类的名称了!)
PS:通过自定义标签的引用形式我们发现只需要定义好标签前缀即表示哪个标签库中的标签。
另外一个属性表示的是TLD(标签描述配置定义)文件的URI。即可以找到这个标签处理接口类的文件目录。
2. 当web应用程序启动时,容器从WEB-INF文件夹的目录结构的META-INF搜索所有以.tld结尾的文件。也就是说它们会定位所有的TLD文件。对于每个TLD文件,容器会先获取标签库的URI,然后为每个TLD文件和对应的URI创建映射关系。在JSP页面中,我们仅需通过使用带有URI属性值的标签库指令来和具体的标签库匹配。
三、自定义标签的处理过程
1.在JSP中引入标签库:
<% @ taglib prefix=”taglibprefix” uri=”tagliburi” %>
2.在JSP中使用标签库标签
3.Web容器根据第二个步骤中的prefix,获得第一个步骤中声明的taglib的uri属性值
4.Web容器根据uri属性在web.xml找到对应的元素
5.从元素中获得对应的元素的值
6.Web容器根据元素的值从WEB-INF/目录下找到对应的.tld文件
7.从.tld文件中找到与tagname对应的元素
8.凑元素中获得对应的元素的值
9.Web容器根据元素的值创建相应的tag handle class的实例
10. Web容器调用这个实例的doStartTag/doEndTag方法完成相应的处理
我的理解:同一个JSP页面会有多个<% @ taglib
prefix=”taglibprefix” uri=”tagliburi” %>这样的标签进来。然后在一个页面的下面可能会出现
四、使用自定义标签的步骤
1.创建标签的处理类(Tag Handler Class)
2.创建标签库描述文件(Tag Library Descrptor File)
3.在web.xml文件中配置元素
4.在JSP文件中引人标签库
五、实例
编写代码之前先掌握一下接口继承的关系图
4.3自定义标签API
是所有自定义标签的父接口,Tag 接口和SimpeTag 是其直接子接口。(见图)
43.2Tag接口
Tag接口是传统标签的父接口,定义了两个重要方法doStartTag()、doEndTag() ;四个常量(EVAL_BODY_INCLUDE、SKIP_BODY、EVAL_PAGE、SKIP_PAGE )。
1、 调用doStartTag()
方法执行后向Web容器返回常EVAL_BODY_INCLUDE、SKIP_BODY 。返回EVAL_BODY_INCLUDE 则执行自定义标签的标签体;返回SKIP_BODY则忽略自定义标签的标签体,直接解释执行自定义标签的结果标记。
PS:这里面我们是定义我们的自定义标签要做什么事情。请看示例
public int doStartTag() {
try {
JspWriter out = pageContext.getOut();
out.print("标签开始hello!");
} catch (Exception e) {
System.out.println(e);
}
return EVAL_BODY_INCLUDE;
}
这个标签要做的就是 打印输出一段标签开始hello!
返回值为:EVAL_BODY_INCLUDE 表示执行自定义标签的标签体。如果你返回的是其他常量的话就可能就啥也不干了。
2、调用doEndTag() 方法、执行后向Web容器返回常量EVAL_PAGE、SKIP_PAGE 。返回EVAL_PAGE 则执行JSP页面位于结束标记后面的JSP代码、返回SKIP_PAGE则忽略JSP页面中位于结束标记后面的所有内容,例如
PS:这个方法表示的是我们的自定义标签结束的时候要做什么处理。示例
//标签结束的时候调用
public int doEndTag() {
try {
JspWriter out = pageContext.getOut();
out.print("标签结束了");
} catch (Exception e) {
System.out.println(e);
}
return EVAL_PAGE;
}
返回的是:EVAL_PAGE 表示会继续执行位于结束标记后面的JSP代码了。如果你返回SKIP_PAGE则后面的代码统一不再调用了。像转向标签估计其源码中就使用了这样的!
有时间看一下
IterationTag继承Tag接口,并增加了doAfterBody() 方法和EVAL_BODY_AGAIN 常量。能通知Web容器是否重复执行标签体内容。当调用doAfterBody() 方法返回EVAL_BODY_AGAIN 时,会重复执行标签,知道返回SKIP_BODY,Web容器才会处理标签的结束标记和调用doEndTag() 。(迭代的概念)
BodyTag接口继承自IterationTag 接口,增加2个方法setBodyContent()、doInitBody() 方法和EVAL_BODY_BUFFERED 常量。当doStartTag()方法返回EVAL_BODY_BUFFERED 常量时,Web容器会创建专门用于捕获标签体运行结果的BodyContent 对象,然后调用标签处理器的setBodyContent() 将BodyContent 对象传入标签处理器,Web容器将标签体的执行结果写入BodyContent 对象中。在标签处理器的后续事件方法中,可以通过保存的BodyContent 的对象的引用来获取标签体的执行结果,然后调用BodyContent对象特有的方法对BodyContent对象的内容进行修改和控制。
注:JSP API提供BodyTag接口的实现类BodyTagSupport 。BodyTagSupport 类实现的BodyTagSupport类的doStartTag方法的返回值为EVAL_BODY_BUFFERED。doAfterBody() 返回值为SKIP_BODY,doEndTag() 返回值为EVAL_PAGE 。
SimpleTag接口为JSP2.0新增标签接口,在SimpleTag中之定义了一个用于处理标签逻辑的doTag()方法,该方法在Web容器执行自定义标签时调用,而且只调用一次即完成传统标签的是否执行标签体、迭代标签等功能。
SimpleTagSupport类是SimpleTag接口的实现类。
5.1 编写带颜色的hello文本 自定义标签
第一步:编写处理类
package hello;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.*;
public class HelloTag
extends TagSupport{
public int doStartTag() {
try {
JspWriter out = pageContext.getOut();
out.print("标签开始hello!");
} catch (Exception e) {
System.out.println(e);
}
return EVAL_BODY_INCLUDE;
}
//标签结束的时候调用
public int doEndTag() {
try {
JspWriter out = pageContext.getOut();
out.print("标签结束了");
} catch (Exception e) {
System.out.println(e);
}
return EVAL_PAGE;
}
}
第二步:定义配置文件TLD
xml version="1.0" encoding="UTF-8"?>
<taglib xmlns=""
xmlns:xsi=""
xsi:schemaLocation=" /web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0tlib-version>
<short-name>myTagLibshort-name>
<uri>/myTaguri>
<tag>
<name>HelloTagname> 标签名称表示
<tag-class>hello.HelloTagtag-class> 交与哪个类来处理
<body-content>emptybody-content>
tag>
taglib>
说明:其中的tld文件名称为myTag.tld名
第三步:定义好配置文件web.xml
<jsp-config>
<taglib>
<taglib-uri>myTagtaglib-uri>
<taglib-location>/WEB-INF/myTag.tldtaglib-location>
taglib>
jsp-config>
第四步:在JSP页面引用此自定义标签进来
<%@ taglib uri="/myTag"
prefix="myTag" %>
<%@ page contentType="text/html;charset=utf-8"
%>
<html>
<myTag:HelloTag/>
body>
html>
过程:从此JSP页面中的uri可以从web.xml中找到相应的tld文件出来。根据我们的TLD文件就可以知道HelloTag标签 是由哪个类来处理的
<name>HelloTagname>
<tag-class>hello.HelloTagtag-class>
5.2 编写带有标签体的自定义标签
要开发带标签体的标签,可实现BodyTag接口,也可从BodyTag接口的实现类BodyTagSupport继承,为简化开发,推荐从BodyTagSupport类继承开发。
PS:请参考上面的类继承图可知
编写标签对应的实现类时,需要重载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则标签体执行完后会继续执行页面中接下来的部分;如果返回SKIP_PAGE,则JSP页面的后续内容将不再执行。
实现类
package body;
import java.sql.Time;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class bodyTag1 extends BodyTagSupport{
private int countNum=0;//循环显示时间的次数
private int 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;
}
//countNum
public int getCountNum() {
return countNum;
}
public void setCountNum(int countNum) {
this.countNum = countNum;
this.currentNum=1;
}
}
配置文件
xml version="1.0" encoding="UTF-8"?>
<taglib xmlns=""
xmlns:xsi=""
xsi:schemaLocation="
/web-jsptaglibrary_2_0.xsd" version="2.0">
<tlib-version>1.0tlib-version>
<short-name>myTagLibshort-name>
<uri>/myTaguri>
<tag>
<name>bodyTag1name>
<tag-class>body.bodyTag1tag-class>
<body-content>JSPbody-content>
<attribute>
<name>countNumname>
<required>truerequired>
<rtexprvalue>truertexprvalue>
attribute>
tag>
taglib>
配置web.xml
<jsp-config>
<taglib>
<taglib-uri>myTagtaglib-uri>
<taglib-location>/WEB-INF/myTag.tldtaglib-location>
taglib>
jsp-config>
引用:
<myTag:bodyTag1 countNum="6"> 属性
现在的时间是:<%=new Date()%><br> 标签体内容
myTag:bodyTag1>
第二个专题:专门整理常用的示例!方便将来的直接开发操作