JSP2开发自定义标签
目录
- JSP2开发自定义标签
- 目录
- 开发自定义标签的步骤
- 开发简单的自定义标签
- 自定义标签处理类
- 标签处理类的声明周期
- 建立TLD文件
- 注册标签
- 使用标签库
- 带属性的标签
- 带有标签体的标签
- 以页面片段作为属性标签
- 动态属性标签
- 源码
1. 开发自定义标签的步骤
JSP2的规范简化了标签库的开发,在原本的JSP1.1中开发自定义标签比较复杂。
JSP2中开发自定义标签只需要如下的几步:
- 开发自定义标签处理类。
- 建立一个*.tld文件,每个文件对应一个标签库,每个标签库可以包含多个标签。
- 在JSP文件中使用自定义标签(测试)。
2. 开发简单的自定义标签
1. 自定义标签处理类
自定义标签类继承一个父类:javax.servlet.tagext.SimpleTagSupport,除此之外,JSP自定义标签还有如下的要求:
- 对于所有的标签类的属性,都要有对应的gettter和setter方法。
- 重写doTag()方法,这个方法负责生成页面内容。
下面是一个简单的输出HelloWorld的标签。
public class HelloWorldTag extends SimpleTagSupport {
// 重写doTag()方法,该方法文便签生成标签页
@Override
public void doTag() throws JspException, IOException {
// 获取页面输出流,并输出字符
getJspContext().getOut().write("Hello World "
+ new java.util.Date());
}
}
上面的标签处理类继承了SimpleTagSupport父类,并重写doTag()方法,而doTag()输出页面的内容。该标签页面没有提供属性,因此无需提供getter和setter方法。
2. 标签处理类的声明周期
- JSP容器通过调用其无参构造,创建一个简单的标签处理类实例。因此,简单的标签处理器必须有一个无参构造器。
- JSP容器调用setJspContext方法,同时传递一个JSPContext对象。JspContext最重要的方法时getOut,它返回一个JspWriter,用于将相应发送到客户端。setJspContext方法的签名如下:
大多数时候,会需要将传进的JspContext付一个类变量,以便后续使用。public void setJspContext(JspContext jspContext)
- 如果表示标签处理类的定制标签是嵌套在另一个标签中的,JSP容器就会调用setParent方法。
- JSP容器为改标签的定义的每个属性都调用设置方法(setter)
- 如果标签中有主体内容,JSP将调用SimpleTag接口的setJspBody方法,将主体内容作为JspFragment。如果没有主体内容,则JSP容器不会调用这个方法。
- JSP容器调用doTag方法,所有变量在doTag方法返回是进行同步。
3. 建立TLD文件。
首先,何为TLD?TLD是Tag Library Definitation的缩写,即标签库文件的定义,文件的后缀是tld,每个TLD文件对应一个标签库,一个标签库中可以包含多个标签。TLD文件也被称为标签库定义文件。每个标签都需要在该文件中注册,类似于Servlet在web.xml中注册。
标签库文件的根元素是taglib,他可以包含多个tag子元素,每一个子元素定义一个标签。Tomcat的Webapps\examples\WEB-INF\jsp2路径下有一个jsp2-example-taglib.tld文件,该文件是一个TLD文件的示例。
可以将该文件复制到WEB-INF/路径下,或者其它的子路径下,复制并修改后的内容如下:
<?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
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>mytaglib</short-name>
<uri></uri>
</taglib>
标签库文件也是一个标准的XML文件,该文件的根元素是taglib元素。
可以在这个文件中注册自定义的标签。
从上面的代码中可以看出taglib有如下的3个子元素。
- tlib-version:指的是该库的版本,这是一个座位标识的内部版本号。
- short-name:该标签的默认的短名,该名称通常没有太大的作用。
- uri:这个属性非常的重要,他指定了该标签库的URI,相当于标签库的唯一标识。JSP页面中使用标签库时就是根据该URI来定位标签库的。URI有两种路径:
- 相对路径:可以在该文件不使用URI,而在JSP文件中直接定位到该文件。(使用自定义标签时会说到)
- 绝对路径:使用网址+路径作为统一资源定位,这个在发布自定义标签时非常重要。
所以,在练习是使用相对路径。当然如果有域名也可以使用绝对路径,如果不想使用相对路径,有没有域名,可以使用http://www.example.com,这是一个可以做测试的一个域名。
打开之后:

翻译过来大概是:该域被建立用于文档中的说明性示例。您可以在没有事先协调或要求许可的情况下使用此域。
4. 注册标签
在taglib还可以包含多个tag元素,每个tag元素可以定义一个标签,tag元素下允许出现如下常用元素:
- name:该标签的名称,在JSP页面中就是根据该名称来使用此标签的。
- tag-class:指定标签的处理类,他指定了标签有那个标签处理类来处理。
- body-content:指定标签体的内容。该元素的值可以是如下几个。
- tagdependent:指定标签处理类自己负责处理标签体。
- empty:指定该标签只能作为空标签使用。
- scriptless:指定该标签的标签体可以是静态的HTML元素、表达式语言(EL),但不允许出现JSP脚本。
-
dynamic-attributes:指定该标签是否支持动态属性。只有当定义动态属性标签时才是用该元素。
下面的程序注册了hello标签,所使用的类是前面出现的类:
<?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
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>mytaglib</short-name>
<uri>http://www.example.com/mytaglib</uri>
<tag>
<!-- 标签的名字 -->
<name>hello</name>
<!-- 标签的处理类 -->
<tag-class>com.example.mytagClass.HelloWorldTag</tag-class>
<!-- 定义标签体 -->
<body-content>empty</body-content>
</tag>
</taglib>
5. 使用标签库
在JSP页面中使用标签库需要注意如下两点:
- 标签库的URI:确定使用哪个标签库。
- 标签名:确定使用那个标签。
使用标签库导入标签库,使用taglib编译指令导入标签库,就是将标签库和指定的前缀关联起来。
taglib编译指令的语法如下:
<%@ taglib prefix="tagPrefix" uri="tagliburi" %>
其中uri属性指定标签库的URI,这个URI可以确定一个标签库。而prefix属性指定标签库的前缀,即所有使用该前缀的标签将由该标签库处理。
使用标签的语法如下:
<tagPrefix:tagName tagAttribute="tagValue"...>
<tagBody/>
</tagPrefix:tagName>
如果该标签没有标签体,则可以使用如下的语法:
<tagPrefix:tagName tagAttribute="tagValue".../>
上面使用的标签的语法都使用了标签体,而hello标签没有任何属性,所以该标签只需要使用
<mytag:hello/>
即可。
如下:
<%@ page contentType="text/html;charset=UTF-8"
language="java" %>
<%@taglib prefix="hello" uri="http://www.example.com/taglib" %>
<%-- 如果前面没有指定URI,则可以这样使用 --%>
<%-- <%@taglib prefix="hello" uri="/WEB-INF/mytaglib.tld" %> --%>
<html>
<head>
<title>Hello Tag</title>
</head>
<body>
Hello!!!<br/>
<hello:helloWorld></hello:helloWorld>
</body>
</html>
运行之后可以看到如下效果:
3. 带属性的标签
前面说的标签即没有属性,也没有标签题,用法、功能都比较简单。
下面的就是一个带有属性的标签:
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.StringTokenizer;
public class DataFormatterTag extends SimpleTagSupport {
private String header;
private String item;
public String getHader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
@Override
public void doTag() throws JspException, IOException {
JspContext jspContext = getJspContext();
JspWriter out = jspContext.getOut();
out.println("<table style='border:1px solid green'>\n"
+ "<tr><td><span style='font-weight:blod'>"
+ header + "</span></td></tr>");
StringTokenizer tokenizer = new StringTokenizer(item, ",");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
out.print("<tr><td>" + token + "</td><tr>\n");
}
out.print("</table>");
}
}
前面说过每个属性都必须设置setter和getter,所以上面的header和item都有getter和setter方法,要开发的这个标签的作用是,获取一个表头和item生成一个表格。其中item是一个用“,”隔开的字符串,然后使用StringTokenizer解析成items属性。
接下来就是注册该标签了.
对有属性的标签,需要在<tag…/>元素增加<attribute…/>子元素,每个attribute子元素定义了一个标签属性。<attribute…/>子元素通常还需用定义如下的几个元素。
- name:设置属性名,则元素的值是字符串的内容。
- reqired:设置该属性是否必须属性,该子元素的值true或false。
- fragment:设置该属性是否支持JSP脚本、表达式等动态语言、子元素的值是true或false。
下面的是为上面的DataFormatterTag注册,在mytablib.tld文件中增加如下配置片段。
<tag>
<name>dataFormatter</name>
<tag-class>com.example.myTag.DataFormatterTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>header</name>
<required>true</required>
<fragment>false</fragment>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<fragment>false</fragment>
</attribute>
</tag>
上面的代码分别配置了header和items属性。
下面就是使用该标签了。
<%@ page contentType="text/html;charset=UTF-8"
language="java" %>
<%@taglib prefix="simple" uri="/WEB-INF/mytaglib.tld" %>
<html>
<head>
<title>测试DataFormatterTag</title>
</head>
<body>
<simple:dataFormatter header="字母" items="A,B,C,D"/>
<br/>
<simple:dataFormatter header="国家">
<jsp:attribute name="items">
美国,中国,加拿大
</jsp:attribute>
</simple:dataFormatter>
</body>
</html>
上面的代码使用了两次的dataFormatter标签,并且以两种不同的方式传递属性,一种是标签属性,一种使用的attribute标准动作指令。
下面的是他的效果:
4. 带有标签体的标签
带有标签体的标签可以切入其他内容。通常用于完成一些逻辑运算,例如循环和判断等。下面以一个迭代器标签为示例,介绍下。
首先定义一个标签处理类:
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.Collection;
public class IteratorTag extends SimpleTagSupport {
// 标签属性,用于指定需要被迭代的集合
private String collection;
// 标签属性,指定被迭代的集合元素,为集合元素指定名称
private String item;
public String getCollection() {
return collection;
}
public void setCollection(String collection) {
this.collection = collection;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
// 处理标签的方法,在标签处理类中只需要重写doTag()方法
@Override
public void doTag() throws JspException, IOException {
// 从page scope中获取名为collection的集合
Collection itemList = (Collection)getJspContext().getAttribute(collection);
// 遍历集合
for (Object s : itemList) {
// 将集合元素设置到page范围内
getJspContext().setAttribute(item, s);
// 输出标签体
getJspBody().invoke(null);
}
}
}
上面的标签处理类与之前的类基本一样,只是在遍历collection的时就调用getJspBody()方法,该方法返回标签所包含的标签体:JspFragment对象,执行invoke()方法,即可输出标签体的内容。该标签的作用是:每遍历一个集合元素,即输出标签体一次。
因为标签体不为空,所以配置是指定body-content为sctiptless,注册该标签的代码如下:
<tag>
<!-- 定义标签名 -->
<name>iterator</name>
<!-- 定义标签处理类 -->
<tag-class>com.example.myTag.IteratorTag</tag-class>
<!-- 定义标签体不允许出现JSP脚本 -->
<body-content>scriptless</body-content>
<!-- 配置标签属性:collection -->
<attribute>
<name>collection</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<!-- 配置标签属性 -->
<attribute>
<name>item</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
<tag>
上面的代码中指定该标签体可以是静态的HTML内容,也可以是表达式语言,但不允许是JSP脚本。
为了测试在JSP页面中的使用效果,下面将一个List对象设置成page范围的属性,然后该标签来迭代输出LIst集合的全部元素。
<%@ page contentType="text/html;charset=UTF-8"
language="java" %>
<%@taglib prefix="mytag" uri="/WEB-INF/mytaglib.tld"%>
<html>
<head>
<title>测试带标签体的标签</title>
</head>
<body>
<h2>带有标签体的标签-迭代器标签</h2>
<%
// 创建一个List对象
List<String> a = new ArrayList<String>();
a.add("中国");
a.add("美国");
a.add("英国");
// 将List对象放进page范围
pageContext.setAttribute("a", a);
%>
<table border="1" bgcolor="#7fffd4" width="300">
<!-- 使用迭代器标签,对a集合进行迭代 -->
<mytag:iterator item="item" collection="a">
<tr>
<td>${pageScope.item}</td>
</tr>
</mytag:iterator>
</table>
</body>
</html>
下面是该便签的测试效果图:
5. 以页面片段作为属性标签
JSP2规范的自定义标签还允许直接将一段“页面片段”作为属性,这种方式给自定义标签提供了更大的灵活性。
以“页面片段”为属性的标签与普通的标签区别并不大,在该标签处理类定义一个JSPFragment类型的属性,即表明该标签允许使用“页面片段”类型的属性。
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
public class FragmentTag extends SimpleTagSupport {
private JspFragment fragment;
public JspFragment getFragment() {
return fragment;
}
public void setFragment(JspFragment fragment) {
this.fragment = fragment;
}
@Override
public void doTag() throws JspException, IOException {
JspWriter out = getJspContext().getOut();
out.println("<div style='padding:10px;border:1px solid black;"
+ ";border-radius:20px'>");
out.println("<h3>下面是动态传入的JSP片段</h3>");
// 调用、输出“页面片段”
fragment.invoke(null);
out.println("</div>");
}
}
上面程序中定义的fragment成员变量,该成员变量代表了使用该标签时的“页面片段”。所以注册是与普通并无任何区别。
<tag>
<!-- 定义标签名 -->
<name>fragment</name>
<!-- 定义标签处理类 -->
<tag-class>com.example.myTag.FragmentTag</tag-class>
<!-- 指定标签不支持标签体 -->
<body-content>empty</body-content>
<!-- 定义标签属性:fragment -->
<attribute>
<name>fragment</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
由于该标签需要一个fragment属性,该属性的类型是JspFragment,因此使用该标签时需要使用<jsp:attribute…/>动作指令来设置属性值,如下所示:
<%@ page contentType="text/html;charset=UTF-8"
language="java" %>
<%@taglib prefix="mytag" uri="/WEB-INF/mytaglib.tld" %>
<html>
<head>
<title>测试:以页面片段作为标签属性</title>
</head>
<body>
<h2>下面显示的是自定义标签中的内容</h2>
<mytag:fragment>
<jsp:attribute name="fragment">
<%-- 使用后jsp:attribute标签传入fragment参数(改注释不能放在fragment内) --%>
<mytag:helloWorld/>
</jsp:attribute>
</mytag:fragment>
<br/>
<mytag:fragment>
<jsp:attribute name="fragment">
<!-- 下面是动态的JSP页面 -->
${pageContext.request.remoteAddr}
</jsp:attribute>
</mytag:fragment>
</body>
</html>
由于程序制定fragment标签的标签体为empty,因此程序中国fragment开始和Fragment结束标签之间只能使用<jsp:attribute…/>子元素,甚至注释都不行。
效果如下图所示:
6. 动态属性标签
有时候标签的属性个数是不确定的,属性名也不确定,这时候就需要借助动态属性标签了。
动态属性标签有两个额外的要求:
- 标签的处理类还需要实现DynAttribute接口
- 配置便签时通过<dynamic-attributes…/>指定该标签所支持的动态属性。
下面是一个动态属性标签处理类。
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.ArrayList;
public class DynaAttributesTag extends SimpleTagSupport
implements DynamicAttributes {
// 保存每个属性名的集合
private ArrayList<String> keys = new ArrayList<>();
// 保存每个属性值的集合
private ArrayList<Object> values = new ArrayList<>();
@Override
public void doTag() throws JspException, IOException {
JspWriter out = getJspContext().getOut();
// 此处只是简单的输出每个属性值
out.println("<ol>");
for (int i = ; i < keys.size(); i++) {
String key = keys.get(i);
Object value = values.get(i);
out.println("<li>" + key + " = " + value + "</li>");
}
out.println("</ol>");
}
@Override
public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
// 添加属性名
keys.add(localName);
// 添加属性值
values.add(value);
}
}
上面的标签属性处理类实现了DynamicAttribute接口,该方法用于为该标签处理类动态的添加属性名和属性值。标签的属性名和属性值分别私用ArrayList<String>类型的keys和ArrayList<Object>类型的values保存。
注册该标签时需要额外的指定<dynamic-attributes…/>子元素,来表明该标签是动态属性的标签。下面是该属性的注册代码片段:
<tag>
<name>dynaAttr</name>
<tag-class>com.example.myTag.DynaAttributesTag</tag-class>
<body-content>empty</body-content>
<!-- 指定支持动态属性 -->
<dynamic-attributes>true</dynamic-attributes>
</tag>
一旦定义了动态属性的标签,接下来在页面中使用该属性时将十分的灵活,完全可以在为该标签设置任意的属性。如下:
<%@ page contentType="text/html;charset=UTF-8"
language="java" %>
<%@taglib prefix="mytag" uri="/WEb-INF/mytaglib.tld" %>
<html>
<head>
<title>测试动态属性的标签</title>
</head>
<body>
<h2>下面显示的是自定义标签的内容</h2>
<h4>指定两个属性</h4>
<mytag:dynaAttr name="你的名字" age="10000"/>
<h4>指定4个属性</h4>
<mytag:dynaAttr name="星武者" age="20" work="暂无" birthday="****年**月**日"/>
</body>
</html>
下面是该标签的效果:
7. 源码
想要获得源码点击下方按钮:
按钮