自定义JSP标签有两种方式:传统的自定义标签开发和简单标签开发。
传统的自定义标签开发:在传统的自定义标签的主体中可以包含Java程序片段,标签的处理流程比较复杂,现在一般不使用。
简单标签开发:为了简化开发标签的过程,JSP2.0引入了一种新的标签扩展机制,称为“简单标签扩展”。
简单标签的开发可以选择实现javax.servlet.jsp.tagext.SimpleTag接口,也可以继承SimpleTagSupport类。这种方式十分类似Servlet的开发,我们开发一个Servlet可以选择实现Servlet接口,也可以直接继承HttpServlet类,实际中更多地采用继承HttpServlet类。简单标签的开发实际中也是继承SimpleTagSupport类。
不过为了更好地理解整个过程,我们还是从SimpleTag接口开始讲起。
1. 实现SimpleTag接口:
在javax.servlet.jsp.tagext.SimpleTag接口中定义了以下5个方法。
① void setJspContext(JspContext pc):由Servlet容器调用该方法,向SimpleTag对象传递当前的JspContext对象。JspContext类是PageContext类的父类,在JspContext类中定义了用于存取各种范围内的共享数据的方法,如getAttribute( )、setAttribute( )和removeAttribute( )方法等。
② void setParent(JspTag parent) :由Servlet容器调用该方法,向当前SimpleTag对象传递父标签的JspTag对象。
③ JspTag getParent():返回父标签的JspTag对象
④ void setJspBody(JspFragment jspBody):由Servlet容器调用该方法,向当前SimpleTag对象传递标签主体。参数jspBody表示当前标签的主体,它封装了一段JSP代码。
⑤ void doTag():该方法负责具体的标签处理过程。与传统标签处理类的doStartTag()和doEndTag()方法不同的是,doTag()方法没有返回值。
SimpleTag对象由Servlet容器负责创建。每次当Servlet容器在执行JSP文件时,遇到JSP文件中的自定义的简单标签,都会创建一个SimpleTag对象,当标签处理完毕,就会销毁该SimpleTag对象,这是与传统的自定义标签的一个不同之处。对于传统的自定义标签,Servlet容器会缓存标签处理类的实例,以便重复利用该实例。
Servlet容器在得到了SimpleTag对象后,会按照下图的流程调用SimpleTag对象的相关方法:
⑴ Servlet容器调用SimpleTag对象的setJspContext( )和setParent( )方法,把当前JSP页面的JspContext对象及父标签处理对象传给当前SimpleTag对象。如果不存在父标签,则把父标签处理对象设为null。
⑵ Servlet容器调用SimpleTag对象的一系列set方法,设置SimpleTag对象的属性。如果标签没有属性,则无需这个步骤。
⑶如果存在标签主体,Servlet容器就调用SimpleTag对象的setJspBody()方法,设置标签主体。
⑷Servlet容器调用SimpleTag对象的doTag()方法,在该方法中完成处理标签的具体逻辑。
2. 继承SimpleTagSupport类:
实际开发中我们更多地是选择直接继承SimpleTagSupport类,并覆盖它的doTag()方法来开发自定义标签。
我们先来看看SimpleTagSupport类的源代码:
public class SimpleTagSupport implements SimpleTag
{
private JspTag parentTag;
private JspContext jspContext;
private JspFragment jspBody;
public SimpleTagSupport() { }
public void doTag() throws JspException, IOException
{
}
public void setParent( JspTag parent ) {
this.parentTag = parent;
}
public JspTag getParent() {
return this.parentTag;
}
public void setJspContext( JspContext pc ) {
this.jspContext = pc;
}
protected JspContext getJspContext() {
return this.jspContext;
}
public void setJspBody( JspFragment jspBody ) {
this.jspBody = jspBody;
}
protected JspFragment getJspBody() {
return this.jspBody;
}
public static final JspTag findAncestorWithClass(
JspTag from, Class<?> klass)
{
boolean isInterface = false;
if (from == null || klass == null
|| (!JspTag.class.isAssignableFrom(klass)
&& !(isInterface = klass.isInterface()))) {
return null;
}
for (;;) {
JspTag parent = null;
if( from instanceof SimpleTag ) {
parent = ((SimpleTag)from).getParent();
}
else if( from instanceof Tag ) {
parent = ((Tag)from).getParent();
}
if (parent == null) {
return null;
}
if (parent instanceof TagAdapter) {
parent = ((TagAdapter) parent).getAdaptee();
}
if ((isInterface && klass.isInstance(parent))
|| klass.isAssignableFrom(parent.getClass())) {
return parent;
}
from = parent;
}
}
}
可以知道,SimpleTagSupport类实现了SimpleTag接口的所有方法,除了doTag()方法为空实现,其余方法都有具体的实现。所以我们开发自己的自定义标签只需要继承SimpleTagSupport类并覆盖doTag()方法即可。
第一个自定义标签:无标签主体的简单标签
ShowInfo.java:
public class ShowInfoTag extends SimpleTagSupport {
private String info;
private int num;
public void setInfo(String info) {
this.info = info;
}
public void setNum(int num) {
this.num = num;
}
@Override
public void doTag() throws JspException, IOException {
JspContext jc=getJspContext();
PageContext pc=(PageContext)jc;
JspWriter out=pc.getOut();
for(int i=0;i<num;i++){
out.println(info+"<br>");
}
}
}
mytag.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>JSTL 1.1 core library</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>c1</short-name>
<uri>www.dragon.com</uri>
<tag>
<name>showInfo</name>
<tag-class>com.tag.ShowInfoTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>info</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>num</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
test.jsp:
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c1" uri="www.dragon.com" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<c1:showInfo info="hello world" num="10"/>
</body>
</html>
第二个自定义标签:有标签主体的简单标签
HelloUserTag.java:
public class HelloUserTag extends SimpleTagSupport{
private String username;
public void setUsername(String username) {
this.username = username;
}
@Override
public void doTag() throws JspException, IOException {
PageContext pc=(PageContext)getJspContext();
JspWriter out=pc.getOut();
JspFragment jspBody=getJspBody();
if(!username.equals("Tom")){
out.print("Hello,");
jspBody.invoke(null);
}else{
out.print("go out,"+username+"!");
throw new SkipPageException();
}
}
}
mytag.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.1</tlib-version>
<short-name>c1</short-name>
<uri>www.dragon.com</uri>
<tag>
<name>helloUser</name>
<tag-class>com.tag.HelloUserTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>username</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
test.jsp:<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c1" uri="www.dragon.com"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<c1:helloUser username="${param.username}">
${param.username}<br>
</c1:helloUser>
other content......
</body>
</html>
jspBody对象为JspFragment类型。JspFragment类代表一段JSP代码,它的invoke(java.io.Writer out)方法负责执行所封装的JSP代码,并且通过out参数输出执行结果。如果参数out为null,则把执行结果输出到当前输出流中。
对于简单标签,tld文件的<tag>元素的<body-content>元素的可选值包括empty、scriptless,默认为scriptless。对于传统的自定义标签,<body-content>的值可为jsp,但是由于简单标签的主体不能包含Java程序片段等脚本元素,所以简单标签的<body-content>的值不能为jsp。
empty:表示不允许出现标签体
scriptless:允许出现标签体,但不允许出现Java代码,即不允许<% %>、<%! %>、<%= %>。
jsp:允许出现Java代码,但是简单标签不支持。