目录
通过Java的TagSupport类或者BodyTagSupport类,和配套的tld文件,可以定义自己的jsp标签。
TagSupport类和BodyTagSupport类在jsp-api.jar中,这个jar包在tomcat的lib目录下有,maven里面也有。
一,标签库描述文件(tld文件)
tld的全名:Tag Library Descriptor。
tld文件是一个XML文件,是自定义标签库的配置文件。
这个文件应该放在web项目的WEB-INF目录下,或其子目录下,如果放在其他位置,需要在web.xml文件中做配置。
下面是一个简单的标签库描述文件的例子:
myFirstTag.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>testTagLib</short-name>
<uri>/test-tags</uri>
<display-name>"自定义标签"</display-name>
<tag>
<name>TagA</name>
<tag-class>com.mytest.TagA</tag-class>
<body-content>jsp</body-content>
<description>这个一个标签</description>
<attribute>
<name>attributeA</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<description>属性A</description>
</attribute>
</tag>
<tag>
<name>TagB</name>
<tag-class>com.mytest.TagB</tag-class>
<body-content>jsp</body-content>
<description>这个一个标签</description>
<attribute>
<name>attributeA</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<description>属性A</description>
</attribute>
</tag>
</taglib>
文件的主体是<tablib>元素,其中的元素有:
<tlib-version>,taglib版本
<jsp-version>,jsp版本
<short-name>,标签库名字
<uri>,标签库地址,在页面使用标签库时会用到
<display-name>,描述
<tag>,具体的标签列表,可以配置多个
其中的<tag>元素,每个元素代表一个自定义的标签,可以有这些子元素:
<name>,标签名字,在页面使用标签时会用到
<tag-class>,标签处理类
<body-content>,empty、scriptless、JSP、tagdependent
<description>,标签描述,可以配置多个
<attribute>,标签参数列表,可以配置多个
其中的<attribute>元素,每个元素代表一个属性,可以有这些子元素:
<name>,属性名字,在页面使用标签时会用到
<required>,是否必填,只能是true或false
<rtexprvalue>,全称Run-time Expression Value,是否支持EL表达式,只能是true或false。如果设置为true,在页面可以用这种写法:attributeA="<%=name>"
<description>,属性描述
二,标签处理类
标签处理类是在描述文件中用<tag-class>元素标识出的java类,这个类可以选择继承javax.servlet.jsp.tagext.TagSupport类或javax.servlet.jsp.tagext.BodyTagSupport类,其中BodyTagSupport是TagSupport的子类。
如果选择继承TagSupport类,可以重写以下方法:
public int doStartTag()
public int doEndTag()
public int doAfterBody()
如果选择继承BodyTagSupport类,可以额外重写以下方法:
public void setBodyContent(BodyContent b)
public void doInitBody()
下面分别介绍这几个方法:
doStartTag()
这是在java开始处理头标签时执行的方法,可以在这里构造将要输出到页面的代码,或者做其他的处理。
这个方法的返回值是int,有以下选择:
1,EVAL_BODY_INCLUDE。
这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是1。
这个返回值表示正常加载标签的body(也就是标签的innerHTML)。
2,SKIP_BODY。
这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是0。
这个返回值表示不会加载标签的body。一般情况下doStargTag()方法返回这个值后将执行doEndTag()方法。
3,EVAL_BODY_BUFFERED。
这个静态变量在javax.servlet.jsp.tagext.BodyTag接口中,实际值是2。
只有处理类继承了BodyTagSupport时可以使用(详见下面的类继承关系图)。
这个返回值用来在后面的代码中处理标签的body。当使用这个返回值时,Java会构造一个BodyContent对象,并把标签的body载入这个对象。
另外,BodyContent对象内部也有一个JspWriter,可以用BodyContent对对象的body进行处理。是否可以修改body是BodyTagSupport和TagSupport最大的区别。
注意:如果使用了这个返回值并重写了相关方法,需要在代码中显式写明body部分的加载,否则body只会写入BodyContent对象而不会加载到页面。
doEndTag()
这是在java开始处理尾标签时执行的方法,这个方法中也可以构造将要输出到页面的代码,或者其他处理。
这个方法的返回值是int,有以下选择:
1,EVAL_PAGE。
这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是6。
这个返回值表示继续加载此标签之后的页面代码。
2,SKIP_PAGE。
这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是5。
这个返回值表示不再继续加载此标签之后的页面代码,直接将jsp现有已经处理完的页面代码发送给客户端。
没有想到什么情况下用得着这个返回值。
doAfterBody()
这个方法在doStartTag()方法返回EVAL_BODY_INCLUDE并且加载完标签的body后加载。
这个方法的返回值是int,有以下选择:
1,EVAL_BODY_AGAIN。
这个静态变量在javax.servlet.jsp.tagext.IterationTag接口中,实际值是5。
这个返回值表示再次加载标签的body。
按照执行顺序,再次加载一次body之后会再次执行这个方法,注意不要写成死循环。
2,SKIP_BODY。
同doStartTag()方法的同名返回值。
java不会再加载标签的body,向后执行。一般情况下后面将执行doEndTag()方法。
setBodyContent(BodyContent b)
这个方法是在调用doStartTag()方法,并返回EVAL_BODY_BUFFERED之后调用的。BodyTagSupport子类专用。
Java构造了BodyContent对象之后调用这个方法把BodyContent对象传给处理类,BodyTagSupport类中这个方法里面只有一行:
this.bodyContent = b;
如果重写了这个方法,至少也要把这行加上,否则无法使用BodyContent对象。
该方法没有返回值。
doInitBody()
该方法在调用setBodyContent(BodyContent b)方法后执行,可以对body进行一些初始化工作。BodyTagSupport子类专用。
BodyTagSupport类中这个方法里面没有代码。
这个方法没有返回值。
上述类和接口的关系图大概是这样的:
当java处理标签时,根据返回值的不同,方法的调用顺序大概是这样的:
三,在JSP页面中使用自定义的标签
1,在JSP页面中,首先要引用标签库:
<%@ taglib prefix="t" uri="/test-tags"%>
prefix是标签前缀,和标签库中的<short-name>不一定相同。
uri参数和标签库中的<uri>必须相同。
2,引用了标签库之后,就可以使用自定义的标签了:
<testTagLib:TagA attributeA="extra">
number2:<input id='number2' name='number2' value='222' />
</testTagLib:TagA>
上面的例子就是有body的标签,body就是
number2:<input id='number2' name='number2' value='222' />
这一部分,通过调整方法的返回值可以控制该部分是否显示。
四,举个例子
1,首先是tld标签描述文件
myFirstTag.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>testTagLib</short-name>
<uri>/test-tags</uri>
<display-name>"自定义标签"</display-name>
<tag>
<name>TagA</name>
<tag-class>com.mytest.TagA</tag-class>
<body-content>jsp</body-content>
<description>这是一个标签</description>
<attribute>
<name>attributeA</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<description>属性A</description>
</attribute>
</tag>
<tag>
<name>TagB</name>
<tag-class>com.mytest.TagB</tag-class>
<body-content>jsp</body-content>
<description>这是一个标签</description>
<attribute>
<name>attributeA</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<description>属性A</description>
</attribute>
</tag>
</taglib>
在这个描述文件中定义了两个标签,TagA和TagB
2,标签TagA的处理类TagA.java
TagA.java
package com.mytest;
import java.io.IOException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.TagSupport;
public class TagA extends BodyTagSupport{
private static final long serialVersionUID = 1L;
protected String attributeA;
public String getAttributeA() {
return attributeA;
}
public void setAttributeA(String attributeA) {
this.attributeA = attributeA;
}
@Override
public int doStartTag(){
JspWriter w=this.pageContext.getOut();
StringBuffer sb = new StringBuffer();
sb.append("<form id='aform' action='");
sb.append(attributeA);
sb.append(".do' method='post'>");
sb.append("number1:<input id='number1' name='number1' value='111' /><br/>");
try {
w.print(sb.toString());
w.flush();
} catch (IOException e) {
e.printStackTrace();
}
if("extra".equals(attributeA)){
return EVAL_BODY_INCLUDE;
// return EVAL_BODY_BUFFERED;
}else{
return SKIP_BODY;
}
}
@Override
public void setBodyContent(BodyContent bodyContent) {
System.out.println("setBodyContent...");
this.bodyContent = bodyContent;
}
@Override
public void doInitBody() throws javax.servlet.jsp.JspException {
};
@Override
public int doAfterBody() throws javax.servlet.jsp.JspException {
String a="";
if(this.bodyContent!=null&&a.equals("")){
String html=this.bodyContent.getString();
System.out.println(html);
return SKIP_BODY;
}else{
return SKIP_BODY;
}
}
@Override
public int doEndTag(){
JspWriter w=this.pageContext.getOut();
StringBuffer sb = new StringBuffer();
sb.append("<br/><button type='submit'>提交</button>");
sb.append("</form>");
try {
w.print(sb.toString());
w.flush();
} catch (IOException e) {
e.printStackTrace();
}
if("extra".equals(attributeA)){
return EVAL_PAGE;
}else{
return SKIP_PAGE;
}
}
}
这个类重写了doStartTag(),doEndTag()和doAfterBody()等方法,其中doAfterBody()方法里面没写什么东西,和父类的原方法保持一致。
这个处理类的功能是把自定义的标签写成了一个form表单,并添加了名为number1的文本框,当属性attributeA等于extra时显示number2文本框,并且正常加载标签后面的网页内容,否则不会显示number2文本框,并且立即结束页面加载,发送给客户端。
3,JSP页面
testTagPage.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="testTagLib" uri="/test-tags"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>TestTagPage</title>
</head>
<body>
extra
<testTagLib:TagA attributeA="extra">
number2:<input id='number2' name='number2' value='222' />
</testTagLib:TagA>
number3:<input id='number3' name='number2' value='333' />
<hr/>
no extra
<testTagLib:TagA attributeA="noextra">
number2:<input id='number2' name='number2' value='222' />
</testTagLib:TagA>
number3:<input id='number3' name='number2' value='333' />
</body>
</html>
这个页面显示出来是这样的:
分析一下:
当java第一次加载TagA标签时(分隔线上面部分),属性attributeA等于extra,
doStartTag()方法返回值是EVAL_BODY_INCLUDE,表示正常加载body部分的页面代码,
doEndTag()方法返回值是EVAL_PAGE,表示正常加载标签后面的内容,所以第二个TagA标签才得以加载。
当java第二次加载TagA标签时(分隔线下面部分),属性attributeA等于noextra,
doStartTag()方法返回值是SKIP_BODY,表示不会加载body部分的页面代码,
doEndTag()方法返回值是SKIP_PAGE,表示不会加载标签后面的内容,所以第二个TagA标签后面的number3标签和</body></html>标签都没有加载。
浏览器实际收到的页面代码是这样的:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>TestTagPage</title>
</head>
<body>
extra
<form id='aform' action='extra.do' method='post'>number1:<input id='number1' name='number1' value='111' /><br/>
number2:<input id='number2' name='number2' value='222' />
<br/><button type='submit'>提交</button></form>
number3:<input id='number3' name='number2' value='333' />
<hr/>
no extra
<form id='aform' action='noextra.do' method='post'>number1:<input id='number1' name='number1' value='111' /><br/><br/><button type='submit'>提交</button></form>
以上就是自定义JSP标签的介绍,通过此功能可以组建自己更强大更高效的标签库。