[toc]

 

简单标签和标签文件

传统自定义标签的方法较为复杂,需要考虑处理类中doStartTag()和doEndTag()的返回值。JSP2中引入了新的标签扩展机制,称为“简单标签扩展”。

可以通过定义实现javax.servlet.jsp.tagext.SimpleTag接口的标签处理类,

或使用标签文件.tag或.tagx来定义标签。

 

1. 实现SimpleTag接口

javax.servlet.jsp.tagext.SimpleTag接口,包中已有SimpleTagSupport类实现了接口,开发时只需继承该类,复写doTag()方法即可。

1.1 接口定义方法

  1. void setJspContext(JspContext pc):容器调用,向SimpleTag对象传递当前JSPContext对象。JspContext类是PageContext类的父类。其中包含了对各种范围内属性的存取方法
  2. void setParent(JspTag parent):容器调用,向SimpleTag对象传递父标签的JSPTag对象
  3. JspTag getParent():返回父标签的JspTag对象
  4. void setJspBody(JspFragment jspBody):容器调用,向SimpleTag对象传递标签主体,参数表示主体,封装了一段JSP代码
  5. void doTag():负责具体标签处理过程,没有返回值

1.2 接口流程

SimpleTag对象由容器负责创建。当容器执行JSP文件时,遇到自定义的简单标签时,都会创建一个SimpleTag对象。标签处理完毕,就销毁该对象。而传统自定义标签,会缓存处理类实例以重用。

流程:

  1. 容器调用SimpleTag对象的setJspContext()和setParent()方法,把当前JSP页面的JspContext对象,以及父标签处理对象传给当前SimpleTag对象。如无父标签,则null
  2. 容器调用SimpleTag对象的一系列set方法,设置对象的属性。如标签无属性则无需
  3. 如有标签主体,容器调用setJSPBody()方法,设置主体
  4. 容器调用doTag()方法,完成处理标签的具体逻辑

 

1.3 普通的简单标签

<hello>标签,输出"This is my first tag!"。

简单标签处理类:

1
2
3
4
5
public class HelloTag extends SimpleTagSupport {       
public void doTag() throws JspException, IOException {
getJspContext().getOut().write("This is my first tag!");
}
}

使用该标签的JSP例子:

1
2
3
4
<%@ taglib prefix="mm" uri="/mytaglib" %>
...
<p><b>My first tag prints</b>: <mm:hello/>
...

运行步骤:

  1. 编译HelloTag.java,编译时把<CATALINA_HOME>/lib/jsp-api.jar放下classpath中,把编译生成类文件放在web-app/WEB-INF/classes/mypack/HelloTag.class

  2. 在web-app/WEB-INF/mytag.tld中添加标签描述符

    1
    2
    3
    4
    5
    6
    <tag>
    <description>prints hello</description>
    <name>hello</name>
    <tag-class>mypack.HelloTag</tag-class>
    <body-content>empty</body-content>
    </tag>

    注:简单标签的<body-content>元素,可选值empty、scriptless、tagdependent,由于不能包含Java片段,所以没有jsp选项

  3. 在web-app/WEB-INF/web.xml中加入mytaglib标签库

    1
    2
    3
    4
    <taglib>
    <taglib-uri>/mytaglib</taglib-uri>
    <taglib-location>/WEB-INF/mytaglib.tld</taglib-location>
    </taglib>

     

1.4 带属性和标签主体的简单标签

<welcome>标签,将属性username和主体一同输出。

处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class WelcomeTag extends SimpleTagSupport {  

private String username;
public void setUsername(String username){
this.username=username;
}

public void doTag() throws JspException, IOException {
getJspContext().getOut().print(username+",");

JspFragment jspBody=getJspBody();
// 将主体的执行结果输出到当前输出流中
jspBody.invoke(null);
}
}

注:JspFragment对象,代表一段JSP代码,其invoke(Writer)方法负责执行所封装的JSP代码,并通过参数输出结果;若参数null,则输出到当前输出流

使用标签:

1
2
3
<mm:welcome username="${param.username}">
Welcome to my website.
</mm:welcome>

web-app/WEB-INF/mytag.tld:

1
2
3
4
5
6
7
8
9
10
11
<tag>
<description>welcome</description>
<name>welcome</name>
<tag-class>mypack.WelcomeTag</tag-class>
<body-content>tagdependent</body-content>
<attribute>
<name>username</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

 

1.5 带动态属性的简单标签

<max>标签,负责找出属性中最大值,存入page范围。

动态属性:数目不固定的属性。

处理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MaxTag extends SimpleTagSupport implements DynamicAttributes{  

private ArrayList<String> al=new ArrayList<String>();

public void setDynamicAttribute(String uri,String localeName,Object value)throws JspException{
// 所有属性保存到ArrayList中
al.add((String)value);
}

public void doTag() throws JspException, IOException {
JspContext context=getJspContext();

int max=0;
for(int i=0;i<al.size();i++){
int num=Integer.parseInt(al.get(i));
max= num > max ? num : max;
}
// max保存到page
context.setAttribute("max",new Integer(max));
}
}

注:实现了javax.servlet.jsp.tagext.DynamicAttributes接口,定义了setDynaminAttribute()方法;容器反复调用此方法向SImpleTag对象传递动态属性。

使用标签:

1
2
<mm:max num1="100" num2="200" num3="300"/>
MAX: ${max}

web-app/WEB-INF/mytag.tld:

1
2
3
4
5
6
7
<tag>
<description>max</description>
<name>max</name>
<tag-class>mypack.MaxTag</tag-class>
<body-content>empty</body-content>
<dynaminc-attributes>true</dynaminc-attributes>
</tag>

 

2. 使用标签文件

  1. 标签文件使用JSP编写,可以不包含Java。拓展名通常为.tag,若使用XML语言则为.tagx
  2. 标签名字就是文件名字
  3. 标签文件和JSP的区别仅在于:
    1. JSP中page指令在标签文件中不能使用
    2. 标签文件中增加了tag指令、attribute指令和variable指令
    3. <jsp:invoke>和<jsp:doBody>两个标准动作元素只能在标签文件中使用
  4. 使用时标签文件可统一放在webapp/WEB-INF/tags目录下
  5. 标签文件的自定义标签方法,无需TLD,只需在使用的JSP页面中声明即可 <%@ taglib prefix="mm" tagdir="/WEB-INF/tags" %>
  6. 容器自动为tags和子目录配置隐含TLD,包含以下元素:
    1. <tlib-version>:1.1
    2. <short-name>:路径名设定,WEB-INF/tags,则为tags;WEB-INF/tags/a/b/c,则为a-b-c
    3. 为该目录中每个标签文件配置<tag-file>元素,子元素<name>设置文件名,<path>设置标签文件路径,路径以/WEB-INF开始
  7. 标签文件打包JAR时,放在META-INF/tags下,并提供TLD,<path>以/META-INF开始
  8. 容器遇到标签时,解析并编译.tag标签文件,在<CATA_LINA>/work/Catalina/localhost/webapp/org/apache/jsp/tag/web下生成处理类name_tag类,即实际上标签文件仍是简单标签处理类,只是由容器翻译

 

2.1 标签文件隐含对象

request(HttpServletRequest):存于request范围

response(HTTPServletResponse):存于page

jspContext(JspContext):page

application(ServletContext):application

out(JspWriter):page

config(ServletConfig):page

session(HttpSession):session

与JSP相比,标签文件不存在page和exception隐含对象,存在jspContext对象,是JSP中的pageContext的父类。

 

2.2 标签文件的指令

包含taglib。include、tag。attribute、variable五种指令,后三种是特有。

  1. tag指令:与JSP中的page指令类似,设置整个标签文件的一些属性
    1. display-name:为标签指定一个简短名字,默认不含扩展名的文件名
    2. body-content:指定标签主体格式,可选empty、scriptless、tagdependent,默认scriptless
    3. dynamic-attributes:指定动态属性名,容器翻译为简单标签处理类时,类中创建Map对象,存放动态属性的名字和值,属性名key,属性值value
    4. small-icon:为标签指定小图标文件gif jpeg的路径,16*16
    5. large-icon:为标签指定大图标文件gif jpeg的路径,32*32
    6. description
    7. example:提供使用本标签的例子的信息描述
    8. language:设定编程语言,默认Java
    9. import:引入Java类
    10. pageEncoding:文件的字符编码
    11. isELIgnored;是否忽略EL表达式,默认false,会解析EL表达式
  2. attribute指令:类似TLD中<attribute>元素,声明自定义标签的属性,指令包含以下属性
    1. name:对于本指令是必需,指定指令所代表的属性名字
    2. required:本属性是否必需,默认false
    3. fragment:本属性是否是JspFragment对象,默认false;若true,则无需设置rtexpvalue和type属性,此时二者默认为true和JspFragment
    4. rtexpvalue:本属性是否可以是一个运行时表达式,默认true
    5. type:本属性类型,默认String,需要是类,不能是基本型
    6. description
  3. variable指令:类似于TLD中的<variable>元素,设置标签为JSP页面提供变量
    1. name-given:指定变量名字
    2. name-from-attribute:用标签的某个属性的值作为变量名称
    3. alias:定义一个本地范围的属性保存这个变量的值,当name-from-attribute时必须alias
    4. variable-class:变量的Java类型,默认String
    5. declare:是否引用新对象,默认true
    6. scope:变量的范围,可选AT_BEGIN(从标签起始标记到JSP页面结束)、NESTED(标签主体范围)、AT_END(从标签结束标签到JSP页面结束),默认NESTED
    7. description

 

2.3 <jsp:invoke>和<jsp:doBody>动作元素

  1. <jsp:invoke>用于执行标签的JspFragment类型的属性所包含的JSP代码,并把执行结果输出到当前JspWriter对象中,或保存到指定的命名变量中
    1. fragment:必需属性,指定类型为JspFragment的属性名称
    2. var:指定一个命名变量名字,保存JspFragment对象执行结果
    3. varReader:制定一个Reader类型的命名变量,保存了JspFragment执行结果
    4. 如以上两保存属性都无,则输出到当前JspWriter对象中
    5. scope:为var或varReader指定存放范围
  2. <jsp:doBody>用于执行标签主体,并把结果输出到当前JspWriter对象中,或保存到指定命名变量中,有var、varReader、socpe属性

 

2.4 带属性和标签主体的标签文件

本例简单的display.tag,定义了一个表格模板,包含color、bgcolor、title属性,都是String类型,在标签文件中可以通过${color}的形式访问属性。

标签文件 display.tag:

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ attribute name="color" %>
<%@ attribute name="bgcolor" %>
<%@ attribute name="title" %>
<table border="0" bgcolor="${color}">
<tr>
<td>${title}</td>
</tr>
<tr>
<td bgcolor="${bgcolor}">
<jsp:doBody/>
</td>
</tr>
</table>

使用标签:

1
2
3
<tags:display color="#ff000" bgcolor="#ffc0c0" title="Travel">
Travel Headline.
</tags:display>

 

2.5 使用JspFragment的标签文件

本例welcome.tag,根据属性username,输出不同文字。

标签文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ tag pageEncoding="GB2312" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ attribute name="username" required="true" fragment="true" %>

<jsp:invoke fragment="username" var="user" />

<c:choose>
<c:when test="${empty user}">
My friend,
</c:when>
<c:otherwise>
${user},
</c:otherwise>
</c:choose>

<jsp:doBody/>

通过<jsp:invoke>执行JspFragment类型的username属性,并把结果存放在命名变量user中。然后根据user,输出不同文字。

使用标签:

1
2
3
4
5
6
7
8
<mm:welcome>
<jsp:attribute name="username">
${param.username}
</jsp:attribute>
<jsp:body>
Welcome to my website.
</jsp:body>
</mm:welcome>

因为属性username是Fragment类型,就需要<jsp:attribute>设定;同时,标签若嵌入了<jsp:attribute>,就要用<jsp:body>设置标签主体。

 

2.6 带变量的标签文件

JSP页面和标签之间交互数据方法:

1. 共享数据放在特点范围内
2. 在JSP页面中设置标签的属性,实现JSP向标签的数据传递
3. 在标签中定义变量,实现标签向JSP页面的数据传递

简单标签文件的variable指令用于定义标签变量。

本例precode.tag,具有一个code变量

1
2
3
4
5
6
7
8
<%@attribute name="preserve" fragment="true" %> 
<%@variable name-given="code" scope="NESTED" %>

<jsp:doBody var="code" />

<table border="1"><tr><td>
<pre><jsp:invoke fragment="preserve"/></pre>
</td></tr></table>

把标签主体的结果存放在code中,<jsp:invoke>执行preserve包含的JSP代码,把结果输出到当前JspWriter中。

使用标签:

1
2
3
4
5
6
7
8
9
<mm:precode>
<jsp:attribute name="preserve">
<b>${code}</b>
</jsp:attribute>

<jsp:body>
${fn:toUpperCase(" Tomcat ")}
</jsp:body>
</mm:precode>

标签doBody,将TOMCAT存入code变量;然后invoke执行preserve属性包含的JSP代码,输出code的值,最终页面输出TOMCAT。