自订标签库--TagSupport详解
1 TagSupport与BodyTagSupport的区别
TagSupport与BodyTagSupport的区别主要是标签处理类是否需要与标签体交互,如果不需要交互的就用TagSupport,否则就用BodyTagSupport。
交互就是标签处理类是否要读取标签体的内容和改变标签体返回的内容。
用TagSupport实现的标签,都可以用BodyTagSupport来实现,因为BodyTagSupport继承了TagSupport。
2 如果在自订标签库时,并不需要对标签本体作处理,则您可以继承TagSupport类别,TagSupport类别实作了Tag与IterationTag介面,IterationTag介面则是Tag介面的子介面,TagSupport类别替您预先实作了这两个介面中的方法,在继承TagSupport之后,您视需要来改写当中的一些方法(这是 Default Adapter模式)。
Tag介面中与标签处相关的方法主要有:doStartTag()、doEndTag()。其中doStartTag()方法是遇到标签开始时会呼叫的方法,其合法的回传值是EVAL_BODY_INCLUDE与SKIP_BODY,前者表示将显示标签间的本体文字,后者表示不显示标签间的本体文字;doEndTag()方法是在遇到标签结束时呼叫的方法,其合法的回传值是EVAL_PAGE与SKIP_PAGE,传回前者表示处理完标签后继承执行以下JSP网页,传回后者则表示不处理接下来的JSP网页。
IterationTag介面继承Tag介面,在标签处理上增加了doAfterBody(),这个方法是在显示完标签间文字本体之后呼叫的,其合法的回传值有EVAL_BODY_AGAIN与SKIP_BODY,如果传回前者,则会再显示一次标签间的文字本体,传回后者则继承执行标签处理的下一步。
TagSupport类别实作了Tag与IterationTag介面,其预设的回传值是:doStartTag()回传 SKIP_BODY、doAfterBodyTag()回传SKIP_BODY、doEndTag()回传EVAL_PAGE。所以在继承 TagSupport之后,如果没有改写任何的方法,则在标签处理上执行的顺序是:
如果您改写了doStartTag(),则您必须指定回传值,如果指定了EVAL_BODY_INCLUDE,则预设会照以下的顺序执行:
doStartTag() -> 显示本体文字 -> doAfterBodyTag()->doEndTag()->执行接下来的JSP网页
简单的说,如果您继承了TagSupport并改写了其中的一些方法,则您必须指定方法合法的回传值,如果不改写,则会有其预设的回传值,/以下作个流程整理:

下面这个程式简单的示范如何使用自订标签来对网页内容作一些保护:
- GuardTag.java
package onlyfun.caterpillar;
import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
public class GuardTag extends TagSupport {
public int doStartTag() throws JspException {
String valid = pageContext.getRequest().getParameter("valid");
// 如果flag设定为key,就显示本体文字内容
if(valid.equals("valid_user")) {
return EVAL_BODY_INCLUDE;
}
return SKIP_BODY;
}
}
同样的,程式编译完之后,放置在WEB-INF/classes/之下,然后编译.tld档案,如下:
- guardtag.tld
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd" version="2.0">
<description>Tag Demo</description>
<tlib-version>1.0</tlib-version>
<jsp-version>2.0</jsp-version>
<short-name>TagDemo</short-name>
<uri>/TagDemo</uri>
<tag>
<description>Cuard BodyText</description>
<name>guard</name>
<tag-class>onlyfun.caterpillar.GuardTag</tag-class>
<body-content>JSP</body-content>
</tag>
</taglib>
在<body-content>中设定的是JSP,这表示如果本体中包括JSP网页的内容,将会被编译执行,接下来您可以在web.xml中定义uri与.tld的名称对应关系,方法与前一个主题相同,这边就不重复说明了,然后撰写一个测试的JSP网页:
- test.jsp
<%@taglib prefix="caterpillar" uri="http://caterpillar.onlyfun.net/"%>
<html>
<body>
<caterpillar:guard>
${ param.user }, 您好,幸运密码是 oooxxx !
</caterpillar:guard>
</body>
</html>
为了要能看到幸运密码,您必须在请求中包括guard参数,假设请求是:
这样就可以看到幸运密码了:
<body>
这个网页包括受包护的内容OOOXXXX。。。。。。<p>
Justin, 您好,幸运密码是: oooxxx !
</body>
</html>
这是部份防护网页内容的一个简单例子,您还可以在doEndTag()中使用SKIP_PAGE来防护标签以下的整个网页,这使得您不用在JSP网页中包括防护的判断逻辑。
====================================================================================================================================
一、基本概念:
1.标签(Tag):
标签是一种XML元素,通过标签可以使JSP网页变得简洁并且易于维护,还可以方便地实现同一个JSP文件支持多种语言版本。由于标签是XML元素,所以它的名称和属性都是大小写敏感的
2.标签库(Tag library):
由一系列功能相似、逻辑上互相联系的标签构成的集合称为标签库。
3.标签库描述文件(Tag Library Descriptor):
标签库描述文件是一个XML文件,这个文件提供了标签库中类和JSP中对标签引用的映射关系。它是一个配置文件,和web.xml是类似的。
shortname: 指定Tag Library默认的前缀名(prefix)
uri: 设定Tag Library的惟一访问表示符 ,uri用来指定Tag Library的标识符,它必须和web.xml中的属性保持一致。
prefix 表示在JSP网页中引用这个标签库的标签时的前缀
4.标签处理类(Tag Handle Class):
标签处理类是一个Java类,这个类继承了TagSupport或者扩展了SimpleTag接口,通过这个类可以实现自定义JSP标签的具体功能
二、自定义JSP标签的格式:
<% @ taglib prefix=”someprefix” uri=”/sometaglib” %>
为了使到JSP容器能够使用标签库中的自定义行为,必须满足以下两个条件:
1.从一个指定的标签库中识别出代表这种自定义行为的标签
2.找到实现这些自定义行为的具体类
第一个必需条件-找出一个自定义行为属于那个标签库-是由标签指令的前缀(Taglib Directive's Prefix)属性完成,所以在同一个页面中使用相同前缀的元素都属于这个标签库。每个标签库都定义了一个默认的前缀,用在标签库的文档中或者页面中插入自定义标签。所以,你可以使用除了诸如jsp,jspx,java,servlet,sun,sunw(它们都是在JSP白皮书中指定的保留字)之类的前缀。
uri属性满足了以上的第二个要求。为每个自定义行为找到对应的类。这个uri包含了一个字符串,容器用它来定位TLD文件。在TLD文件中可以找到标签库中所有标签处理类的名称
<someprefix:sometag key=”somevalue”/>
当web应用程序启动时,容器从WEB-INF文件夹的目录结构的META-INF搜索所有以.tld结尾的文件。也就是说它们会定位所有的TLD文件。对于每个TLD文件,容器会先获取标签库的URI,然后为每个TLD文件和对应的URI创建映射关系。
在JSP页面中,我们仅需通过使用带有URI属性值的标签库指令来和具体的标签库匹配
三、自定义JSP标签的处理过程:
1.在JSP中引入标签库:
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方法完成相应的处理
四、创建和使用一个Tag Library的基本步骤:
1.创建标签的处理类(Tag Handler Class)
2.创建标签库描述文件(Tag Library Descrptor File)
3.在web.xml文件中配置元素
4.在JSP文件中引人标签库
五、TagSupport类简介:
1.处理标签的类必须扩展javax.servlet.jsp.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进行初始化
六、TagSupport处理标签的方法:
1.TagSupport类提供了两个处理标签的方法:
public int doStartTag() throws JspException
public int doEndTag() throws JspException
2. doStartTag:但JSP容器遇到自定义标签的起始标志,就会调用doStartTag()方法。
doStartTag()方法返回一个整数值,用来决定程序的后续流程。
A.Tag.SKIP_BODY:表示…之间的内容被忽略
B.Tag.EVAL_BODY_INCLUDE:表示标签之间的内容被正常执行
3.doEndTag:但JSP容器遇到自定义标签的结束标志,就会调用doEndTag()方法。doEndTag()方法也返回一个整数值,用来决定程序后续流程。
A.Tag.SKIP_PAGE:表示立刻停止执行网页,网页上未处理的静态内容和JSP程序均被忽。略任何已有的输出内容立刻返回到客户的浏览器上。
B.Tag_EVAL_PAGE:表示按照正常的流程继续执行JSP网页
七、ServletContext类的常用API:
1.getContext(String uripath):返回服务器中uripath所代表的ServletContext对象
2.getInitParameter(String name):返回ServletConfig对象中name参数的值
3.getMineType(String file):返回file参数代表的文件的MIME类型
4.getRequestDispatcher(String path):返回path代表的RequestDispacher对象
5.getResourceAsStream(String path):以输入流的形式返回path对应的资源,在输入留中对象可以为任意形式的数据,path参数必须以“/”开始且相对于Context Root
八、如何使用ServletContxt读取并保存属性文件:
1.创建java.util.Properties类对象
2.获取ServletContext对象
3.将属性文件以输入流的形式读入到某个输入流对象中
4.将输入流对象加载到Properties对象中
5.将Properties对象保存到ServletContext对象中
-----------------------------------------------------------------------------------------------------------------------------------------------------------
自定义标签还有几种常见的情况,分别为:
一、有体的标记 (如,<x:label>...</x:label>,"..."为标签的body)
二、标记体中使用了表达式 (如,<x:label> ${movie} </x:label>, "${movie}"为标签的体中出现的EL表达式)
三、循环执行标签体
四、含有属性的简单标签 (如,<x:label movie="${movie}" />)
以下,将分别介绍这几种情况:
情况一:编写的是有体(body)的标记,如:
<myTags:simple2>
This is the body //这个就是标记的body
</myTags:simple2>
那么在这种情况下,为了执行body内的语句就需要加入这样一句话到doTag()方法中:
getJspBody().invoke(null);
invoke的意思是“处理标记的body,并把它打印到响应(response)中”。
null的意思是把内容输出到响应(response),而不是输出到什么别的书写器(Writer)上。
除此以外,TLD中的 “<body-content>empty</body-content>” 一栏也需要改动,要改为:
<body-content>scriptless</body-content>
之后会介绍四种不同的body-content的参数。
情况二、如果标记体使用了表达式,如:
<myTags:simple3>
Message is : ${message}
</myTags:simple3>
那么这个表达式的赋值需要在标签处理器的doTag()中完成,如:
getJspContext().setAttribute("message","wear sunscreen");
getJspBody().invoke(null);//一定要记得写这句,否则标签体不会执行
情况三、若想要循环输出一个集合的数据,应该如何实现?如:
<table>
<myTags:simple3>
<tr><td>${movie}</td></tr>
</myTags:simple3>
</table>
显然,希望通过循环输出不同的movie来形成 一个表格。那么标记处理器的doTag()方法应该改为:
String[] movies = {"Monsoon Wedding", "Saved!", ".. ..."};
public void doTag() throws JspException, IOException {
for(int i = 0; i < movies.length; i++){
getJspContext().setAttribute("movie",movies[i]);
getJspBody().invoke(null);//每次invoke,都会执行一次标签body
}
}
情况四、如果这个简单标记是有属性的,怎么办?如:
<myTags:simple5 movieList="${movieCollection}">
<tr>
<td>${movie.name}</td>
<td>${movie.genre}</td>
</tr>
</myTags:simple5>
</table>
由于标记中出现了属性,所以TLD中的描述必须反映这一情况,因此TLD应调整为:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jsee/web-jsptagLibrary_2_0.xsd"
version="2.0">
<tlib-version>1.2</tlib-version>
<uri>simpleTags</uri>
<tag>
<name>simple1</name>
<description>xxxxxxxx</description>
<tag-class>foo.SimpleTagTest1</tag-class>
<body-content>empty</body-content>
<attribute>
<name>movieList</name>
<required>true</required><!-- 说明movieList属性是必需的 -->
<rtexprvalue>true</rtexprvalue><!-- 说明movieList属性可以是一个运行时表达式(不用非得是一个常量String) -->
</attribute>
</tag>
</taglib>
另外,在标记处理器类中,也要对这一属性有相应的体现:
public class SimpleTagTest5 extends SimpleTagSupport{
private List movieList;
public void setMovieList(List movieList){
this.movieList = movieList;
}
public void doTag() ....
}
补充:
<body-content></body-content>中可以写入的参数有四种:
① empty 即标记体为空
② scriptless 这个标记不能有脚本元素,但可以有模板文本和EL, 还可以是定制和标准动作
③ tagdependent 标记体要看做是纯文本,所以不会计算EL,也不会出发标记/动作
④ JSP 能放在JSP中的东西都能放在这个标记体中

浙公网安备 33010602011771号