一、概述
JSP 2.0 中提供了两种新的开发自定义标记的方法:
1、简单标签机制SimpleTag
JSP 2.0 中加入了新的创建自定义标记的API:javax.servlet.jsp.tagext.SimpleTag,该API 定义了用来实现简单标记的接口。和JSP 1.2 中的已有接口不同的是,SimpleTag 接口不使用doStartTag()和doEndTag()方法,而提供了一个简单的doTag()方法。这个方法在调用该标记时只被使用一次。一个自定义标记中实现的所有逻辑都在这个方法中实现。相对JSP1.2 中自定义标记机制,SimpleTag 的方法和处理周期要简单得多。
2、 标签文件
标签文件允许JSP 网页作者使用JSP 语法创建可复用的标签库。标签文件的扩展名必须是.tag。
1.1 使用简单标签机制
与JSP1.2 相似,开发自定义标签要遵循“开发标记类—配置TLD 文件—-在JSP 中使用”的过程,
示例如下:
步骤一:编写标记处理类AddTag.java
package tag;
import java.io.IOException;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class AddTag extends SimpleTagSupport{
private int num1 = 0;
private int num2 = 0;
public void setNum1(int num1) {
this.num1 = num1;
}
public void setNum2(int num2) {
this.num2 = num2;
}
public void doTag() throws JspException, IOException {
JspContext ctx = getJspContext();
JspWriter out = ctx.getOut();
int sun = num1+num2;
out.print(num1+”+”+num2+”=”+sun);
}
}
步骤二:编写描述符文件 test.tld:放在/WEB-INF/test-tld/test.tld下
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN”
“http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd”>
<taglib> <!– 标签定义成<test:add /> –>
<tlibversion>1.0</tlibversion>
<jspversion>1.2</jspversion>
<shortname>test</shortname> <!– 这个test可以设置为空,你标签就可以定义成<add />了,不过一般比较常见的都是<test:add />这种类型的 –>
<tag>
<name>add</name>
<tagclass>tag.AddTag</tagclass>
<bodycontent>empty</bodycontent> <!– 就是<test:add ></test>中间的内容是空的 –>
<info>Add Tag</info>
<attribute>
<name>num1</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>num2</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
步骤三:在JSP 中使用标记:
<%@ page language=”java” pageEncoding=”UTF-8″%>
<%@ taglib prefix=”test” uri=”/WEB-INF/test-tld/test.tld” %>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>测试自定义标签</title>
</head>
<body>
SimpleTag 测试:<br />
<h1><test:add num1=”2″ num2=”3″/> </h1>
</body>
</html>
无需在web.xml下配置,
运行结果如下:
SimpleTag 测试
2+3=5
1.2 使用标签文件
通过标签文件实际上可以将一个JSP 文件的内容作为标签处理程序,但该文件扩展名必须是.tag,
示例如下:
1) 标记文件hello.tag,该文件存放在 WEB-INF/tags 目录下
hello.tag.<br>
IP:<%= request.getRemoteAddr() %>
2) 在JSP 中使用tag 文件
<%@ page contentType=”text/html;charset=gb2312″ %>
<%@ taglib prefix=”test” tagdir=”/WEB-INF/tags/” %>
<h2>Tag File 测试</h2>
<test:hello/>
3) 运行效果如下:
Tag File 测试
hello tag
IP:127.0.0.1
二 、自定义标签简介
2.1 自定义标签概念
JSP标签分为标准JSP 环境自带的标签(即前面章节中学习过的JSP 动作标签)和JSP 自定义标签。JSP 自定义标签是用户定义的标记,它遵循XML 语法。当servlet 容器处理自定义标记时,会自动调用一个Java 类文件完成相对应的功能。
Java 开发人员编写标记处理程序类以处理标记并处理所有需要的Java 代码和数据操作。对于Web页面设计者来说,自定义标记与标准HTML 标记使用起来没什么区别,但HTML 标记只能完成前台显示的功能,而自定义标记可以在后台完成某些操作。
正确编写自定义标记可以让 Web 设计者创建、查询和操作数据而无需编写一行Java 代码。正确使用自定义标记使 Java 开发人员不必再在编码过程中考虑表示层。这样应用程序开发小组的每一位成员都可以关注于他或者她最擅长的事物。
所以说,JSP 自定义标记为在动态Web 页中将表示与业务逻辑分离提供了一种标准化的机制,使页面设计者可以将注意力放到表示上,而应用程序开发人员编写后端的代码。
2.2 标签相关概念
JSP 自定义标签的使用语法与普通HTML标签相同,与自定义标签相关的基本术语简单说明如下,
这些术语在开发JSP 自定义标签时要用到:
1) 自结束标签——没有标记体的标签
示例:<test:myhrtag />
说明:假设myhrtag 是一个自定义标签
2) 属性
示例:<test:myhrtag color=”red” />
说明:以上标签中包含了color 属性,值为red
3) 带标记体的标签
示例:<test:myhrtag > xxxxx </test:myhrtag>
说明:以上标签中间的xxxxx 即为标记体
4) 子标记
示例: <test:myhrtag >
<test:mytag2/>
</test:myhrtag>
说明:以上myhrtag 标签中间的mytag2 即为子标记
2.3 如何创建自定义标签
自定义标签功能的实现要求在后台必须有一个相关的JAVA 类的支持,但并不是任意编写一个JAVA 类就能处理JSP 标签,这个类也必须实现指定的规范才能用于支持JSP 标签,这些规范表现形式也是接口和类,它们在javax.servlet.jsp.tagext包中声明,主要接口/类的描述如下:
javax.servlet.jsp.tagext.Tag 接口,所有处理JSP 标签的类必须实现该接口。该接口中声明了6个方法,如果直接从该接口生成类则必须实现所有的6 个方法,通常不会直接通过该接口生成标签的处理类。
javax.servlet.jsp.tagext.TagSupport 类,该类实现了Tag 接口,用于创建不带标记体的自结束标签,这些标签中可以带属性。
javax.servlet.jsp.tagext.BodyTagSupport 类,该类继承了TagSupport,用于创建带标记体的标签。
通常我们自定义的标签,编写处理程序时使用TagSupport 和BodyTagSupport 即可,不需要涉及到标签体的,继承TagSupport,需要用标签体的,用BodyTagSupport,以下是开发和使用一个JSP 自定义标签的全过程:
1) 开发标记处理类,编译生成class 文件,该类要继承TagSupport 或BodyTagSupport;
2) 创建标记库描述符文件*.tld,在该文件中为标记处理类指定标签名、声明标签属性;
3) 在JSP 中引用标签库;
4) 在JSP 中使用标JSP 标签
三、自结束标签(不带标签体,TagSupport)
3.1 自结束标签简介
这是一种不带标记体的标签,所以该类标签的处理类直接继承javax.servlet.jsp.tagext.TagSupport即可。TagSupport 的主要方法如下:
public int doStartTag() throws JspException
在WEB 容器遇到标签开始时,该方法会运行。
public int doEndTag() throws JspException
在WEB 容器遇到标签结束时,该方法会运行。
TagSupport 类中有一个重要的成员:pageContext,该成员的功能与JSP 的内置对象pageContex完全相同。通过该对象可以得到其他几个JSP 对象的引用。这样,我们就可以在JAVA 类中与JSP 进行交互了。如: JspWriter out=pageContext.getOut();这一语句可以得到JSP 内置对象out 的引用,通过out 我们就可以向客户端浏览器中输出内容了。要使用其他几个JSP 对象原理与此相同。
3.2 自结束标签开发示例
方式二:动态引用
步骤一:编写标记处理类
package tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
/**
* 自定义自结束标签,不含标签体。
*
* 1、在自定义的类中,重写了父类TagSupport 的两个方法:doStartTag()、doEndTag(),在容器遇到标记开始时会运行doStartTag(),遇到标记结束时运行doEndTag()方法;
* 2、doStartTag()方法的返回值:通常可以取两个值:
* EVAL_BODY_INCLUDE——包含标记体,本例中要编写自结束标记所以不使用该值;
* SKIP_BODY——跳过标记体,即不处理标记体,开发自结束标记应该使用该值。
* 3、doEndTag()方法的返回值:通常可以取两个值:
* SKIP_PAGE——返回这个值,则终止页面执行;
* EVAL_PAGE——返回该值则处理完当前标记后,JSP 页面中止运行。
*/
public class MyHrTag extends TagSupport{
/**
*
*/
private static final long serialVersionUID = 1L;
/*
* 在WEB 容器遇到标签开始时,该方法会运行。
* 该方法可以自行定义,也可以不定义。不定义该方法则遇到开始标签什么都不做
* */
public int doStartTag() throws JspException {
try {
//得到网络输出流,pageContext 是从父类继承过来的成员
JspWriter out = pageContext.getOut();
//向网页输出内容
out.println(“<h4>开始执行doStartTag()……</h4>”);
//输出5 条水平线;
for(int i=1; i<=5; i++){
out.println(“<hr>”);
}
} catch (Exception e) {
e.printStackTrace();
}
//return EVAL_BODY_INCLUDE; //处理标记体
return Tag.SKIP_BODY; //跳过标记体;
}
/*
* 在WEB 容器遇到标签结束时,该方法会运行。
* 该方法可以自行定义,也可以不定义。不定义该方法则遇到结束标签什么都不做
* */
public int doEndTag() throws JspException {
try {
JspWriter out=pageContext.getOut();
out.println(“<h3>开始执行doEndTag()…..</h3>.”);
} catch (Exception e) {
e.printStackTrace();
}
//return Tag.SKIP_PAGE; //返回这个值,则终止页面执行;
return EVAL_PAGE;
}
}
步骤二:创建标记库描述符文件myhr.tld,该文件要存放在 WEB-INF/test-tld 目录下:
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN”
“http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd”>
<!–
解释:
在tld 文件中,映射了标记名和处理程序类;
<tallib>元素,代表开始一个标记库的描述
<tligversion>元素,代表标记库的版本
<jspversion>元素,代表标记所支持的JSP 的版本
<shortname>为标记库起别名,相当于注释,无实际用途
<tag>元素,代表开始描述一个标记,其下子元素如下:
<name>——为标记处理类起的标记名
<tagclass>——指定标记处理类的全名(即带包的名字)
<bodycontent>——标记体的类型,该示例中不需要标记体,所有设置为EMPTY,该值的其他取值在后续内容中讲解
–>
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>myhr</shortname> <!– 标签以<myhr:XXX />形式 –>
<tag>
<name>MyHr</name> <!– 该标签为<myhr:MyHr /> –>
<tagclass>tag.MyHrTag</tagclass>
<bodycontent>EMPTY</bodycontent>
</tag>
</taglib>
步骤三:在JSP 中引用标记库描述文件
引用标记库有两种方式,分别称为静态引用和动态引用。
方式一:动态引用(即直接在JSP 页面中使用TLD 文件)
<%@ page language=”java” pageEncoding=”UTF-8″%>
<%@ taglib uri=”/WEB-INF/test-tld/myhr.tld” prefix=”myhr” %>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>My JSP ‘MyHr1.jsp’ starting page</title>
</head>
<body>
<myhr:MyHr/>
</body>
</html>
JSP 指令<%@ taglib… %>用于引用标记库,该指令的两个属性作用如下:
uri——指明要引用的标记库,在静态引用中就是TLD 文件的路径
prefix——为标记起的前缀名,可以防止多个标记重名的情况出现
方式二:静态引用
首先在web.xml 中为TLD 文件声明别名:
<?xml version=”1.0″ encoding=”ISO-8859-1″ ?>
<Web-app>
……
<taglib>
<taglib-uri>myhr2</taglib-uri>
<taglib-location>/WEB-INF/myhr.tld</taglib-location>
</taglib>
……
</Web-app>
然后在JSP 中通过别名引用TLD 文件:
<%@ page language=”java” pageEncoding=”UTF-8″%>
<%@ taglib uri=”myhr2″ prefix=”myhr” %>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>My JSP ‘MyHr1.jsp’ starting page</title>
</head>
<body>
<myhr:MyHr/>
</body>
</html>
到此为止,自结束标签开发完毕,其中主要工作有两个:开发标记处理类、配置TLD 文件。访
问JSP,运行结果如下:
开始执行doStartTag()
开始执行doEndTag()
测试完毕
四、标签中的属性
4.1 为自定义标签添加属性
以上的示例中开发了一个简单的JSP 标签,但一个实用的标签通常还要由属性来制定标签的特定行为,以下示例演示为自定义标签添加属性。该示例在1.2 示例基础上,为标签添加了color 和loop两个自定义属性,以控制水平线的颜色和输出的水平线的数量。
步骤一:编写标记处理类
package tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
/**
*
* 该类为自定义标签添加了两个属性,属性的声明与javabean 语法完全相同,即属性本身是private类型,
* 但要求提供public 的get、set 方法。
*
*/
public class MyHrTag2 extends TagSupport{
//声明属性
private String color = “black”; //定义线条颜色
private String loop = “1”; //定义输出水平线的条数
//严格按照javabean模式
public void setColor(String color) {
this.color = color;
}
//严格按照javabean模式
public void setLoop(String loop) {
this.loop = loop;
}
//只定义遇到开始标签执行方法即可了。
public int doStartTag() throws JspException {
try {
//得到网络输出流
JspWriter out = pageContext.getOut();
//向网页输出内容;
out.println(“<h4>开始执行doStartTag()……</h4>”);
int n = Integer.parseInt(loop);
for (int i=1;i<=n;i++) {
out.print(“<hr color='”+this.color+”‘ />”);
}
} catch (Exception e) {
e.printStackTrace();
}
return Tag.SKIP_BODY;
}
}
步骤二:创建TLD 文件,本例中是在1.2 中的myhr.tld 文件中进行修改得到:
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN”
“http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd”>
<!–
解释:
在tld 文件中,映射了标记名和处理程序类;
<tallib>元素,代表开始一个标记库的描述
<tligversion>元素,代表标记库的版本
<jspversion>元素,代表标记所支持的JSP 的版本
<shortname>为标记库起别名,相当于注释,无实际用途
<tag>元素,代表开始描述一个标记,其下子元素如下:
<name>——为标记处理类起的标记名
<tagclass>——指定标记处理类的全名(即带包的名字)
<bodycontent>——标记体的类型,该示例中不需要标记体,所有设置为EMPTY,该值的其他取值在后续内容中讲解
<tag>中的子元素
<attribute>用于为标签声明属性,其子元素如下:
<name>——用于指定属性名称
<required>——用于声明该属性是否为必需的,本例中声明color、loop 两个属性都不是必需的。
–>
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>myhr</shortname> <!– 标签以<myhr:XXX />形式 –>
<tag>
<name>MyHr</name> <!– 该标签为<myhr:MyHr /> –>
<tagclass>tag.MyHrTag</tagclass>
<bodycontent>EMPTY</bodycontent>
</tag>
<tag>
<name>MyHr2</name> <!– 该标签为<myhr:MyHr /> –>
<tagclass>tag.MyHrTag2</tagclass>
<bodycontent>EMPTY</bodycontent>
<attribute>
<name>color</name>
<required>false</required>
</attribute>
<attribute>
<name>loop</name>
<required>false</required>
</attribute>
</tag>
</taglib>
步骤三:在JSP 中引用TLD,并使用标签
<%@ page language=”java” pageEncoding=”UTF-8″%>
<%@ taglib uri=”/WEB-INF/test-tld/myhr.tld” prefix=”myhr” %>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>My JSP ‘MyHr3.jsp’ starting page</title>
</head>
<body>
第一次测试(未赋属性值):<br>
<myhr:MyHr2/>
第二次测试(使用两个属性):<br>
<myhr:MyHr2 color=”red” loop=”3″/>
第三次测试(使用一个属性):<br>
<myhr:MyHr2 color=”blue”/>
测试完毕.
</body>
</html>
运行图
第一次测试(未赋属性值)
第二次测试(未赋属性值)
第三次测试(未赋属性值)
4.2 标签综合示例
该示例中创建了一个JSP 标签,用于在JSP 中判断用户是否登录过,如果没登录过则自动转到登录页,这样就可以避免在JSP 中使用代码段进行业务判断了。
第一步:定义标签类LoginTag.java,为标记处理程序,从session 中取出登录标志进行判断用户是否登录过:
package tag;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
public class LoginTag extends TagSupport{
public int doStartTag() throws JspException {
try {
HttpSession session = pageContext.getSession();
Object obj = session.getAttribute(“User”);
//判断是否从未登录过,如果没登录过则转到登录页;
if(obj==null){
((HttpServletResponse)pageContext.getResponse()).sendRedirect(“login.html”);
return SKIP_BODY;
}
} catch (Exception e) {
e.printStackTrace();
}
return Tag.SKIP_BODY;
}
}
第二步:编写login.tld
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN”
“http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd”>
<!–
解释:
在tld 文件中,映射了标记名和处理程序类;
<tallib>元素,代表开始一个标记库的描述
<tligversion>元素,代表标记库的版本
<jspversion>元素,代表标记所支持的JSP 的版本
<shortname>为标记库起别名,相当于注释,无实际用途
<tag>元素,代表开始描述一个标记,其下子元素如下:
<name>——为标记处理类起的标记名
<tagclass>——指定标记处理类的全名(即带包的名字)
<bodycontent>——标记体的类型,该示例中不需要标记体,所有设置为EMPTY,该值的其他取值在后续内容中讲解
<tag>中的子元素
<attribute>用于为标签声明属性,其子元素如下:
<name>——用于指定属性名称
<required>——用于声明该属性是否为必需的,本例中声明color、loop 两个属性都不是必需的。
–>
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>test</shortname> <!– 标签以<myhr:XXX />形式 –>
<tag>
<name>islogin</name> <!– 该标签为<myhr:MyHr /> –>
<tagclass>tag.LoginTag</tagclass>
<bodycontent>EMPTY</bodycontent>
</tag>
</taglib>
第三步:动态调用标签
部分内容如下:
<%@ page language=”java” contentType=”text/html;charset=GB2312″%>
<%@ taglib uri=”/WEB-INF/myhr.tld” prefix=”test” %>
….
<test:islogin/>
<h3>欢迎</h3>
….
4.3 TLD 文件概述
以上示例中创建了标记描述符文件*.TLD,现总结该文件的用法如下:
该文件必须放在 WEB-INF 目录下;
该文件是XML 格式的文件,各元素及功能说明如下:
元素 说明
<taglib> 代表开始一个标记库的描述
<tlibversion> 代表标记库的版本,是自定义的
<jspversion> 代表标记所支持的JSP 的版本
<shortname> 为标记库起别名,相当于注释,无实际用途
<tag> 代表开始描述一个标记
表11-1 标记描述符TLD 文件元素说明
其中<tag>元素中又包含若干子元素,说明如下:
元素 说明
<name> 为标记处理类起的标记名
<tagclass> 指定标记处理类的全名(即带包的名字)
<bodycontent> 标记体的内容类型,如果为EMPTY 代表无标记体
<attribute> 用于为标签声明属性
表11-2 <tag>元素的子元素
<attribute>元素为标签声明属性时,需要两个子元素:
<name> 用于指定属性名称
<required> 用于声明该属性是否为必需的
五、标签中的标记体
5.1 标记体简介
标签的标记体是 JSP 页中出现在自定义标签的开始和结束标签之间的数据,标记体也称正文。操纵其正文的标签称为带标记体的标签(也称为正文标签)。
可以编写标签处理程序对标签的标记体进行操作。要编写标记体标签处理程序,必须实现BodyTag 接口。BodyTag 继承了Tag 的所有方法,而且还实现了另外两个处理正文内容的方法,见下表:
方法 说明
setBodyContent(BodyContent b) bodyContent 属性的 Setter 方法
doInitBody() 对正文内容进行初始化操作
为方便开发,在JSP 类库中为BodyTag 接口提供了实现类:javax.servlet.jsp.tagext.BodyTagSupport。该类继承了TagSupport 并实现了BodyTag 接口。因此,标记体标签处理程序只需要覆盖要使用的方法。BodyTagSupport类中定义了一个protected bodyContent 成员变量及get/setBodyContent()方法,bodyContent 是一个缓冲区,用以保存标记体正文内容。
在一个标签处理类中,BodyTag 的处理流程如下:
1、当容器创建一个新的标签实例后,通过setPageContext 来设置标签的页面上下文。
2、使用setParent 方法设置这个标签的上一级标签,如果没有上一级嵌套,设置为null。
3、设置标签的属性,如果没有定义属性,就不调用此类方法。
4、调用doStartTag 方法,这个方法可以返回以下三者之一:EVAL_BODY_INCLUDE、
EVAL_BODY_BUFFERED、SKIP_BODY,当返回EVAL_BODY_INCLUDE 时,就将标记
体直接写到输出流中,如果返回SKIP_BODY,就不再计算标签的正文,如果返回EVAL_BODY_BUFFERED,就将标记体的正文包含到bodyContent 成员中。
5、调用setBodyContent 设置当前的BodyContent.
6、调用doInitBody,可以在该方法中对BodyContent 进行一些初始化操作.每次计算完Body 后调用doAfterBody,如果返回EVAL_BODY_AGAIN,表示继续处理一次标记体,直到返回SKIP_BODY 才继续往下执行.
7、调用doEndTag 方法,结束标签处理.
5.2 一个简单的带标记体的标签
本示例中创建了一个标签用于在浏览器中输出其标记体内容,且输出的次数由标签的属性决定:
步骤一:编写标记处理类
package tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
*
* doStartTag()中的返回值EVAL_BODY_INCLUDE,可以直接将标签的正文内容输出到浏览器中。
* doAfterBody()在处理完一次正文后会自动执行,
* 该方法如果返回EVAL_BODY_AGAIN,则代表再处理一遍正文(即输出到浏览器),返回SKIP_BODY代表正文处理到此结束。
* 本例中循环向浏览器中输出标记体的正文,直到属性loop 的值小于1。
*
*/
public class TestBodyTag extends BodyTagSupport {
private int loop ; //定义输出标签体的次数属性,比如:2的话就表示连续重复输出标签体2次
public void setLoop(int loop) {
this.loop = loop;
}
public int doStartTag() throws JspException {
if(loop>0){
return EVAL_BODY_INCLUDE; //自动将标签体包含到输出流中,第一次将标签体输出到浏览器中.
}else {
return SKIP_BODY; //跳过标签体,不将标签体包含到输出流,不处理标签体,直接忽略.
}
}
public int doAfterBody() throws JspException {
/**
* doAfterBody()在处理完一次正文后会自动执行,
* 该方法如果返回EVAL_BODY_AGAIN,则代表再处理一遍正文(即输出到浏览器),返回SKIP_BODY 代表正文处理到此结束。
* 本例中循环向浏览器中输出标记体的正文,直到属性loop 的值小于1。
*/
if(loop>1){
loop–;
return EVAL_BODY_AGAIN;
}else {
return SKIP_BODY;
}
}
}
步骤二:创建标记库描述符文件testbodytag.tld,该文件要存放在 WEB-INF/test-tld 目录下:
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN”
“http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd”>
<!–
解释:
在tld 文件中,映射了标记名和处理程序类;
<tallib>元素,代表开始一个标记库的描述
<tligversion>元素,代表标记库的版本
<jspversion>元素,代表标记所支持的JSP 的版本
<shortname>为标记库起别名,相当于注释,无实际用途
<tag>元素,代表开始描述一个标记,其下子元素如下:
<name>——为标记处理类起的标记名
<tagclass>——指定标记处理类的全名(即带包的名字)
<bodycontent>——标记体的类型,该示例中不需要标记体,所有设置为EMPTY,该值的其他取值在后续内容中讲解
<tag>中的子元素
<attribute>用于为标签声明属性,其子元素如下:
<name>——用于指定属性名称
<required>——用于声明该属性是否为必需的,本例中声明color、loop 两个属性都不是必需的。
–>
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>test</shortname> <!– 标签以<myhr:XXX />形式 –>
<tag>
<name>bodytag</name> <!– 该标签为<myhr:MyHr /> –>
<tagclass>tag.TestBodyTag</tagclass>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>loop</name>
<required>true</required>
</attribute>
</tag>
</taglib>
步骤三:在JSP 中使用该标记
<%@ page language=”java” pageEncoding=”UTF-8″%>
<%@ taglib prefix=”test” uri=”/WEB-INF/test-tld/testbodytag.tld” %>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>测试自定义带标签体的标签</title>
</head>
<body>
测试带标记体的自定义标记:<br>
<test:bodytag loop=”4″>
这是标记体<br />
</test:bodytag>
</body>
</html>
5.3 一个简单的带标记体的标签(二)
在doStartTag()方法中返回EVAL_BODY_INCLUDE,简单地将标记体的内容直接输出到了浏览器中,并未对内容进行任何处理,在一些业务中有时需要对标记体正文进行处理,以下示例演示了如何读取标记体内容并进行处理:
步骤一:编写标记处理类
package tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
/*
* doStratTag()中的返回值EVAL_BODY_BUFFERED 代表不直接将标记体内容写到输出流中,
* 而是缓存到成员变量bodyContent 中(该成员从BodyTagSupport 继承过来),
* EVAL_BODY_INCLUDE是直接将标记体内容写到输出流中
* */
public class EqualTag extends BodyTagSupport{
private String name = “”;
private String value = “”;
public void setName(String name) {
this.name = name;
}
public void setValue(String value) {
this.value = value;
}
public int doStartTag() throws JspException {
System.out.println(“do starttag()….”);
return EVAL_BODY_BUFFERED; //不直接将标记体内容写到输出流中,而是缓存到成员变量bodyContent 中
}
public void setBodyContent(BodyContent b) {
System.out.println(“setBodycontent()….”+b.getString()+”++”);
this.bodyContent = b; //bodyContent 赋值前,bodyContent是空的
System.out.println(“setBodycontent()….”+bodyContent.getString()+”++”);
}
//初始化标记体
public void doInitBody() throws JspException {
System.out.println(“doInitBody()…..”+bodyContent.getString()+”++”);
}
public int doAfterBody() throws JspException {
System.out.println(“doAfterBody()….”+bodyContent.getString()+”++”);
return SKIP_BODY; //停止包含
}
public int doEndTag() throws JspException { //处理标签体内容,将标签体的内容加租倾斜
System.out.println(“doEndTag()…”+bodyContent.getString()+”++”);
String username = (String)pageContext.getSession().getAttribute(name); //获得存在session的某属性的值,该属性值由标签属性提供获取。
String str = bodyContent.getString();
if(username.equals(value)){ //如果该值与标签属性相等
str = “<b><i>”+str+”</i></b>”; //加粗倾斜,不是的话就原样输出
}
try {
JspWriter out = pageContext.getOut();
out.print(str); //将加粗倾斜后的标签体输出到页面上
this.bodyContent = null;
} catch (Exception e) {
e.printStackTrace();
}
return EVAL_PAGE;
}
}
步骤二:在标记库描述符文件testbodytag.tld,添加tag标记:
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN”
“http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd”>
<!–
解释:
在tld 文件中,映射了标记名和处理程序类;
<tallib>元素,代表开始一个标记库的描述
<tligversion>元素,代表标记库的版本
<jspversion>元素,代表标记所支持的JSP 的版本
<shortname>为标记库起别名,相当于注释,无实际用途
<tag>元素,代表开始描述一个标记,其下子元素如下:
<name>——为标记处理类起的标记名
<tagclass>——指定标记处理类的全名(即带包的名字)
<bodycontent>——标记体的类型,该示例中不需要标记体,所有设置为EMPTY,该值的其他取值在后续内容中讲解
<tag>中的子元素
<attribute>用于为标签声明属性,其子元素如下:
<name>——用于指定属性名称
<required>——用于声明该属性是否为必需的,本例中声明color、loop 两个属性都不是必需的。
–>
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>test</shortname> <!– 标签以<myhr:XXX />形式 –>
<tag>
<name>bodytag</name> <!– 该标签为<myhr:MyHr /> –>
<tagclass>tag.TestBodyTag</tagclass>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>loop</name>
<required>true</required>
</attribute>
</tag>
<tag>
<name>equal</name>
<tagclass>tag.EqualTag</tagclass>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
<attribute>
<name>value</name>
<required>true</required>
</attribute>
</tag>
</taglib>
步骤三:在JSP 中使用该标记
<%@ page language=”java” pageEncoding=”UTF-8″%>
<%@ taglib prefix=”test” uri=”/WEB-INF/test-tld/testbodytag.tld” %>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>测试自定义带标签体的标签</title>
</head>
<body>
<% session.setAttribute(“username”,”tom”); %>
<test:equal name=”username” value=”tom”>session中的username的属性值如果是tom,则这段文字加粗倾斜</test:equal>
<br />
<test:equal name=”username” value=”tom1″>session中的username的属性值如果是tom,则这段文字加粗倾斜</test:equal>
</body>
</html>
运行界面如下:
后台输出如下:
do starttag()….
setBodycontent()….++
setBodycontent()….++
doInitBody()…..++
doAfterBody()….session中的username的属性值如果是tom,则这段文字加粗倾斜++
doEndTag()…session中的username的属性值如果是tom,则这段文字加粗倾斜++
do starttag()….
setBodycontent()….++
setBodycontent()….++
doInitBody()…..++
doAfterBody()….session中的username的属性值如果是tom,则这段文字加粗倾斜++
doEndTag()…session中的username的属性值如果是tom,则这段文字加粗倾斜++
另外,附上各标签的方法的返回值综述:
六、标签中的子标记
一个标签中可以再包含其他的子标记,这种标签称为嵌套标记。创建嵌套标签时,标记处理类与普通标签相似,但在doStartTag()方法中必须返回EVAL_BODY_INCLUDE,JSP 容器才会处理嵌套的子标记。
步骤一:编写顶级标记处理类
package tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class NestTag extends BodyTagSupport{
private String name = “”;
private String value = “”;
public void setName(String name) {
this.name = name;
}
public void setValue(String value) {
this.value = value;
}
public int doStartTag() throws JspException {
String username = (String)pageContext.getSession().getAttribute(name);
if(username.equals(value)){
return EVAL_BODY_INCLUDE; //自动将标签体包含到输出流(因为顶级标签的标签体是一子标签,还要进行该子标签的标签处理)
}else{
return SKIP_BODY; //跳过标签体,不处理
}
}
}
步骤二:在标记库描述符文件testbodytag.tld,添加tag标记:
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN”
“http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd”>
<!–
解释:
在tld 文件中,映射了标记名和处理程序类;
<tallib>元素,代表开始一个标记库的描述
<tligversion>元素,代表标记库的版本
<jspversion>元素,代表标记所支持的JSP 的版本
<shortname>为标记库起别名,相当于注释,无实际用途
<tag>元素,代表开始描述一个标记,其下子元素如下:
<name>——为标记处理类起的标记名
<tagclass>——指定标记处理类的全名(即带包的名字)
<bodycontent>——标记体的类型,该示例中不需要标记体,所有设置为EMPTY,该值的其他取值在后续内容中讲解
<tag>中的子元素
<attribute>用于为标签声明属性,其子元素如下:
<name>——用于指定属性名称
<required>——用于声明该属性是否为必需的,本例中声明color、loop 两个属性都不是必需的。
–>
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>test</shortname> <!– 标签以<myhr:XXX />形式 –>
<tag>
<name>bodytag</name> <!– 该标签为<myhr:MyHr /> –>
<tagclass>tag.TestBodyTag</tagclass>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>loop</name>
<required>true</required>
</attribute>
</tag>
<tag>
<name>equal</name>
<tagclass>tag.EqualTag</tagclass>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
<attribute>
<name>value</name>
<required>true</required>
</attribute>
</tag>
<tag>
<name>nest</name>
<tagclass>tag.NestTag</tagclass>
<bodycontent>JSP</bodycontent> <!– 该声明标记体是其他标记,也可以是Jsp标准标记 –>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
<attribute>
<name>value</name>
<required>true</required>
</attribute>
</tag>
</taglib>
步骤三:在JSP 中使用该标记
<%@ page language=”java” pageEncoding=”UTF-8″%>
<%@ taglib prefix=”test” uri=”/WEB-INF/test-tld/testbodytag.tld” %>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>测试嵌套的标签</title>
</head>
<body>
<% session.setAttribute(“username”,”tom”); %>
<test:nest name=”username” value=”tom”>
<test:bodytag loop=”3″>
session中的用户名是tom<br/>
</test:bodytag>
</test:nest>
</body>
</html>
案例:
show.tld
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE taglib
PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN”
“http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd”>
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>s</short-name>
<tag>
<name>Fen2Yuan</name>
<tag-class>com.buybal.mgr.taglib.Fen2Yuan</tag-class>
<body-content>scriptless</body-content>
<description>分转元</description>
</tag>
<tag>
<name>FenToYuanHasSymbol</name>
<tag-class>com.buybal.mgr.taglib.FenToYuanHasSymbol</tag-class>
<body-content>scriptless</body-content>
<description>分转元带元符号</description>
</tag>
<tag>
<name>NotNull</name>
<tag-class>com.buybal.mgr.taglib.NotNull</tag-class>
<body-content>scriptless</body-content>
<description>空串转换为‘-’</description>
</tag>
</taglib>
struts_taglib.jsp
<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>
<%@ taglib uri=”http://struts.apache.org/tags-bean” prefix=”bean” %>
<%@ taglib uri=”http://struts.apache.org/tags-html” prefix=”html” %>
<%@ taglib uri=”http://struts.apache.org/tags-logic” prefix=”logic” %>
<%@ taglib uri=”http://struts.apache.org/tags-tiles” prefix=”tiles” %>
<%@ taglib uri=”http://java.sun.com/jsp/jstl/core” prefix=”c” %>
<%@ taglib uri=”http://java.sun.com/jsp/jstl/fmt” prefix=”fmt” %>
<%@ taglib uri=”http://java.sun.com/jsp/jstl/functions” prefix=”fn” %>
<c:set var=”ctx” value=”${pageContext.request.contextPath}”></c:set>
<!– 自定义标签 –>
<%@ taglib uri=”/WEB-INF/tag/show.tld” prefix=”s” %>
这样每个jsp文件引进<%@ include file=”/struts_taglib.jsp”%>就可以使用自定义标签了
java类:
package com.buybal.mgr.taglib;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import com.buybal.util.StringUtil;
public class Fen2Yuan extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
JspFragment jf = this.getJspBody();
StringWriter sw = new StringWriter();
jf.invoke(sw);
String content = sw.toString();
if (StringUtil.isEmpty(content)) {
return;
}
BigDecimal ori = new BigDecimal(content);
// 保留2位小数并且四舍五入
ori = ori.divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
getJspContext().getOut().write(String.valueOf(ori));
}
}
public class FenToYuanHasSymbol extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
JspFragment jf = this.getJspBody();
StringWriter sw = new StringWriter();
jf.invoke(sw);
String content = sw.toString();
if (StringUtil.isEmpty(content)) {
return;
}
BigDecimal ori = new BigDecimal(content);
// 保留2位小数并且四舍五入
ori = ori.divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
getJspContext().getOut().write(“¥”+String.valueOf(ori));
}
}
public class NotNull extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
JspFragment jf = this.getJspBody();
StringWriter sw = new StringWriter();
jf.invoke(sw);
String content = sw.toString();
String s = “-“;
if(StringUtil.isNotEmpty(content) && !””.equals(content.trim()) && !”null”.equals(content) ) {
s = content;
}
getJspContext().getOut().write(s);
}
}
jstl常用标签用法:
if标签
<c:if test=”${empty ob.BANKRESPMSG}”>-</c:if>
<c:if test=”${!empty ob.BANKRESPMSG}”>
<s:NotNull>${ob.BANKRESPMSG}</s:NotNull>(<s:NotNull>${ob.BANKRESPCODE}</s:NotNull>-<s:NotNull>${ob.BANKRESPTYPE}</s:NotNull>)
</c:if>
case-when标签
<c:choose>
<c:when test=”${ob.STATUS==’0′}”>已退款</c:when>
<c:when test=”${ob.STATUS==’1′}”>未退款</c:when>
<c:when test=”${ob.STATUS==’2′}”>已拒绝</c:when>
<c:when test=”${ob.STATUS==’3′}”>退款失败</c:when>
<c:when test=”${ob.STATUS==’4′}”>人工核实</c:when>
<c:otherwise>未知</c:otherwise>
</c:choose>
for循环标签
<c:forEach items=”${listinfo}” var=”ob” varStatus=”state”>
<tr>
<td nowrap=”nowrap”><s:NotNull>${ob.TRANFLOW}</s:NotNull></td>
</tr>
</c:forEach>