一、JSP自定义标签简介
标签是一种XML元素,通过标签可以使JSP页面变得简洁并且易于维护,还可以方便的实现同一个JSP文件支持多种语言版本。由于标签是XML元素,所以它的名字和属性都是大小写敏感的
标准JSP标签是用来调用JavaBean组件的操作,处理定向请求以简化JSP页面开发和维护。JSP技术提供了一种封装JSP其它动态类型的机制——自定义标签,它扩展了JSP语言。自定义标签通常发布在标签库中,该库定义了一种自定义标签集并包含实现标签的对象。
自定义标签是用户自定义的JSP语言元素。当JSP页面包含一个自定义标签时被转换为servlet,标签转化为对称为tag handler的对象的操作。接着当servlet执行时Web container调用那些操作。
二、两种标签
可以定义两种类型的标签:
javax.servlet.jsp.tagext.Tag
javax.servlet.jsp.tagext.BodyTag
有标签体的标签必须实现BodyTag接口
Html代码
- < jsptag:map scope =“session” name =“tagMap” >
- body
- </ jsptag:map >
也可能没有标签体:
Html代码
- < jsptag:map />
无标签体的简单标签可以实现Tag接口。
三、标签处理程序
int doStartTag()throws jspException---处理开始标签体
int doEndTag()throws jspException---处理结束标签体
Tag getParent()/void setParent(Tag t)---获得/设置标签的父标签
void setPagContext(PageContext pc)---pageContext属性的setter方法
void release()释放获得的所有资源
doStartTag()和doEndTag()方法的返回值说明:
SKIP_BODY 表示不用处理标签体,直接调用doEndTag()方法。
SKIP_PAGE 忽略标签后面的JSP(SUN企业应用的首选)内容。
EVAL_PAGE 处理标签后,继续处理JSP(SUN企业应用的首选)后面的内容。
EVAL_BODY_INCLUDE 表示在现有的输出流对象中处理标签体,但绕过setBodyContext()和doInitBody()方法
EVAL_BODY_AGAIN 对标签体循环处理。(存在于javax.servlet.jsp.tagext.IterationTag接口中)
实现javax.servlet.jsp.tagext.Tag接口
扩展javax.servlet.jsp.tagext.TagSupport类
TagSupport类定义了get/setParent()和setPageContext(),这与所有标签处理程序几乎相同。
get/setParent()方法允许标签嵌套。
TagSupport类还定义了一个可以被子类使用的pageContext实例变量(protected PageContext pageContext),这个变量是由setPageContext()方法设置的。
在创建自定义标签之前,需要创建一个标签处理程序。标签处理程序是一个执行自定义标签操作的java对象。在使用自定义标签时,要导入一个标签库——即一组标签/标签处理程序对。通过在Web部署描述符中声明库导入它,然后在用指令taglib将它导入JSP页。
Html代码
- < %@ taglib uri = "firstTag" prefix = "my" % >
如果JSP容器在转换时遇到自定义标签,那么它就检查标签库描述符(tag library descripter)(TLD)文件以查找相应的标签处理程序。TLD文件对于自定义标签处理程序,就像Web部署描述符对与servlet一样。
在运行时,JSP页生成的servlet得到对应于这一页面所使用的标签的标签处理程序的一个实例。生成的servlet用传递给它的属性初始化标签处理程序。
标签处理程序实现了生存周期方法。生成的servlet用这些方法通知标签处理程序应当启动、停止或者重复自定义标签操作。生成的servlet调用这些生存周期执行标签的功能。
四、TLD文件
TLD文件的跟元素是taglib。taglib描述了一个标签库——即一组标签/标签处理程序对。
因为我们使用的是JSP版本1.2,所以在这个例子中需要tlib-version和short-name元素。
tlib-version元素对应于标签库版本。
jsp-version对应于标签库所依赖的JSP技术的版本。
short-name元素定义了IDE和其它开发工具可以使用的标签库的简单名。
taglib 元素包含许多 tag 元素,标签库中每一个标签有一个 tag 元素。
五、编写自定义迭代标签和el表达式调用类的静态方法实例
循环标签体类:ForEach.java
Java代码
import java.util.Collection;
import java.util.Iterator;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class ForEach extends BodyTagSupport
{
private String id;
private String collection;
private Iterator iter;
public void setCollection(String collection)
{
this.collection = collection;
}
public void setId(String id)
{
this.id = id;
}
//遇到开始标签执行
public int doStartTag() throws JspException
{
Collection coll = (Collection) pageContext.findAttribute(collection);
// 表示如果未找到指定集合,则不用处理标签体,直接调用doEndTag()方法。
if(coll==null||coll.isEmpty()) return SKIP_BODY;
iter = coll.iterator();
pageContext.setAttribute(id, iter.next());
// 表示在现有的输出流对象中处理标签体,但绕过setBodyContent()和doInitBody()方法
// 这里一定要返回EVAL_BODY_INCLUDE,否则标签体的内容不会在网页上输出显示
return EVAL_BODY_INCLUDE;
}
//在doInitBody方法之前执行,在这里被绕过不执行
@Override
public void setBodyContent(BodyContent arg0)
{
System.out.println("setBodyContent...");
super.setBodyContent(arg0);
}
//此方法被绕过不会被执行
@Override
public void doInitBody() throws JspException
{
System.out.println("doInitBody...");
super.doInitBody();
}
//遇到标签体执行
public int doAfterBody() throws JspException
{
if(iter.hasNext())
{
pageContext.setAttribute(id, iter.next());
return EVAL_BODY_AGAIN;// 如果集合中还有对像,则循环执行标签体
}
return SKIP_BODY;//迭代完集合后,跳过标签体,调用doEndTag()方法。
}
//遇到结束标签执行
public int doEndTag() throws JspException
{
System.out.println("doEndTag...");
return EVAL_PAGE;
}
}
获取VO属性类:GetProperty.java
Java代码
import java.lang.reflect.Method;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class GetProperty extends BodyTagSupport
{
private String name;
private String property;
public void setName(String name)
{
this.name = name;
}
public void setProperty(String property)
{
this.property = property;
}
@SuppressWarnings("unchecked")
public int doStartTag() throws JspException
{
try
{
Object obj = pageContext.findAttribute(name);
if (obj == null) return SKIP_BODY;
Class c = obj.getClass();
//构造GET方法名字 get+属性名(属性名第一个字母大写)
String getMethodName = "get" + property.substring(0, 1).toUpperCase()
+ property.substring(1, property.length());
Method getMethod = c.getMethod(getMethodName, new Class[]{});
pageContext.getOut().print(getMethod.invoke(obj));
System.out.print(property + ":" + getMethod.invoke(obj) + "/t");
} catch (Exception e)
{
e.printStackTrace();
}
return SKIP_BODY;
}
public int doEndTag() throws JspException
{
return EVAL_PAGE;
}
}
表达式直接访问此类中静态的方法:ELFunction.java
Java代码
public class ELFunction
{
public static int add( int i,int j )
{
return i+j;
}
}
写一个测试用的VO类:UserVo.java
Java代码
public class UserVo
{
private String name;
private String password;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}
建好TLD文件tag.tld,放在WEB-INF目录下
Xml代码
<?xml version="1.0" encoding="utf-8"?>
<taglib version="2.0"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:shcemalocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<description>自定义标签</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>firstLabel</short-name>
<uri>http://java.sun.com/jsp/jstl/core</uri>
<!-- 创建自定义 迭代标签 -->
<tag>
<name>forEach</name>
<tag-class>exercise.taglib.ForEach</tag-class>
<!-- 如果没有标签体,设置empty , 如果有标签休必须设置JSP-->
<body-content>JSP</body-content>
<attribute>
<name>id</name>
<required>true</required><!-- 标识属性是否是必须的 -->
<rtexprvalue>true</rtexprvalue><!-- 标识属性值是否可以用表达式语言 -->
</attribute>
<attribute>
<name>collection</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!-- 创建自定义获得属性标签 -->
<tag>
<name>getProperty</name>
<tag-class>exercise.taglib.GetProperty</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>property</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!-- 配置一个表达式调用 的函数 -->
<function>
<name>add</name><!-- 配置一个标签,在JSP页面通过引用前缀调用 -->
<function-class>exercise.taglib.ELFunction</function-class><!-- 实现类 -->
<function-signature>int add(int,int)</function-signature><!-- 静态的方法:包括返回类型,方法名,入参的类型 -->
</function>
</taglib>
在web.xml文件中配置自定义标签
Xml代码
<jsp-config>
<taglib>
<taglib-uri>firstTag</taglib-uri>
<taglib-location>/WEB-INF/tag.tld</taglib-location>
</taglib>
</jsp-config>
在jsp文件中使用标签:tag.jsp
Java代码
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="firstTag" prefix="my"%>
<jsp:useBean id="userVo1" class="exercise.vo.UserVo" scope="request">
<jsp:setProperty name="userVo1" property="name" value="Hackiller"/>
<jsp:setProperty name="userVo1" property="password" value="123"/>
</jsp:useBean>
<jsp:useBean id="userVo2" class="exercise.vo.UserVo" scope="request">
<jsp:setProperty name="userVo2" property="name" value="YangYang"/>
<jsp:setProperty name="userVo2" property="password" value="456"/>
</jsp:useBean>
<%
List list = new ArrayList();
list.add(userVo1);
list.add(userVo2);
pageContext.setAttribute("voList",list);
%>
<html>
<head>
<title>My JSP 'tag.jsp' starting page</title>
</head>
<body>
<h2 align="center">This is my JSP page:测试taglib.</h2>
<hr>
<h2>自定义迭代标签:</h2>
<table>
<tr><td>姓名</td><td>密码</td></tr>
<my:forEach collection="voList" id="uservo">
<tr>
<td><my:getProperty name="uservo" property="name"/></td>
<td><my:getProperty name="uservo" property="password"/></td>
</tr>
</my:forEach>
</table>
<hr>
<h2>表达式调用类的静态方法:</h2>
2+5=${my:add(2,5)}
</body>
</html>