JSTL是标准规范,符合标准的web容器就可以使用JSTL。然而有些要求无法单靠JSTL的标签来完成,也许是要将既有的HTML元素封装加强,或者是为了与应用程序更紧密地结合。比如,希望有个标签,可以直接从应用程序所自定义的对象中取出信息,而不是通过属性来传递对象或信息。
1.Tag File
Tag File是最简单的自定义标签的方式。
编写一个扩展名为.tag的文件(MyTag.tag),并把它放在WEB-INF/tags下:
<%@tag description="描述信息" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
//...
然户可以在需要使用这个Tag File的JSP中使用这个自定义标签:
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<%@taglib prefix="html" tagdir="/WEB-INF/tags" %>
//...
<html:MyTag />
//...
虽然tagdir可以指定Tag File的位置,但实际上只能指定/WEB-INF/tags或其子文件夹下。
创建Tag File时,如同HTML中的元素都有一些属性可以设置一样设置自定义标签的属性,方法是通过attribute指令。
有以下Tag File(header.tag)
<%@tag description="header 內容" pageEncoding="UTF-8"%>
<%@attribute name="title"%>
<head>
<title>${title}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
可以如下使用这个Tag File:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="html" tagdir="/WEB-INF/tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<html:Header title="title info"/>
<body>
//...
</body>
</html>
Tag File是可以有自己的主题内容的。比如:在JSP中,除了<body>和</body>之间的内容不同,其他都是相同的,可以编写这样一个Tag File。(Html.tag)
<%@tag description="HTML 懒人标签" pageEncoding="UTF-8"%>
<%@attribute name="title"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>${title}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<jsp:doBody/>
</body>
</html>
可以这样使用它:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="html" tagdir="/WEB-INF/tags" %>
<html:Html title="title info">
//...
</html:Html>
如果Tag File的标签在使用时有主题内容,默认是不允许有Scriptlet的,因为tag指令的body-content属性默认为scriptless,也就是不可以出现<%%>、<%=%>、
<%!%>。
body-content还可以设置为empty或者tagdependent,empty表示一定没有主体内容,也就是说只能以<html:Html />的形式使用标签,而不能以<html:Html><html:Html />的形式。tagdependent表示将主体中的内容当作纯文本输出,不作任何运算或者转译。
如果你要将自己编写的Tag File打包成jar文件供别人使用,有几个地方需要注意:
tag文件必须放在jar文件的META-INF/tags下或其子文件夹下。
需要定义TLD文件。
TLD文件必须放在jar文件的META-INF/TLDS下。
比如将前面的两个Tag File打包,则需要定义如下TLD文件:
<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>html</short-name>
<uri>http://openhome.cc/html</uri>
<tag-file>
<name>Header</name>
<path>/META-INF/tags/Header.tag</path>
</tag-file>
<tag-file>
<name>Html</name>
<path>/META-INF/tags/Html.tag</path>
</tag-file>
</taglib>
接下来进入放置META-INF的文件夹执行如下命令
jar cvf ../html.jar *
将生成的html.jar放在web应用的WEB-INF/lib下就可以使用自定义标签了。
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="html" uri="http://openhome.cc/html" %>
<html:Html title="title info">
</html:Html>
注意taglib指令的uri属性要对应至TLD文件中的<uri>所设置的名称。
2.Simple Tag
通过实现一个与JSTL中的<c:if>标签功能相似的标签来认识Simple Tag的开发过程。
首先要编写一个标签处理器,这是一个Java类。通过继承javax.servlet.jsp.tagext.SimpleTagSupport来实现,要重写doTag()方法。
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class IfTag extends SimpleTagSupport {
private boolean test;
@Override
public void doTag() throws JspException {
try {
if(test) {
getJspBody().invoke(null);
}
} catch (java.io.IOException ex) {
throw new JspException("IfTag 执行错误", ex);
}
}
public void setTest(boolean test) {
this.test = test;
}
}
如果test属性为true,调用getJspBody()方法,返回一个JspFragment对象,代表<f:if>与</f:if>之间的主体内容。调用JspFragment的invoke()并传入一个null,表示运行<f:if></f:if>之间的主体内容。没有调用invoke(),则主体内容不会运行。
为了让容器了解<f:if>标签与标签处理器类之间的关系,要定义一个TLD文件。
<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>f</short-name>
<uri>http://openhome.cc/jstl/fake</uri>
<tag>
<name>if</name>
<tag-class>cc.openhome.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
</tag>
</taglib>
其中,<required>表示是否一定需要设置这个属性,<rtexprvalue>表示属性是否接受运行时期运算的结果,<type>设置属性的类型。
使用标签时需要在JSP中使用taglib指令。
<%@taglib prefix="f" uri="http://openhome.cc/jstl/fake" %>
接下来将以模仿JSTL的<c:choose>、<c:when>和<c:otherwise>标签,自定义<f:choose>、<f:when>和<f:otherwise>来了解内层标签如何与外层标签沟通。
CHooseTag.java
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class ChooseTag extends SimpleTagSupport {
private boolean matched;
@Override
public void doTag() throws JspException {
try {
this.getJspBody().invoke(null);
} catch (java.io.IOException ex) {
throw new JspException("ChooseTag 执行错误", ex);
}
}
public boolean isMatched() {
return matched;
}
public void setMatched(boolean matched) {
this.matched = matched;
}
}
WhenTag.java
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class WhenTag extends SimpleTagSupport {
private boolean test;
@Override
public void doTag() throws JspException {
try {
JspTag parent = null;
if (!((parent = getParent()) instanceof ChooseTag)) {
throw new JspTagException("必须置于choose标签中");
}
if(((ChooseTag) parent).isMatched()) {
return;
}
if(test) {
((ChooseTag) parent).setMatched(true);
this.getJspBody().invoke(null);
}
} catch (java.io.IOException ex) {
throw new JspException("WhenTag 执行错误", ex);
}
}
public void setTest(boolean test) {
this.test = test;
}
}
Otherwise.java
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class OtherwiseTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException {
try { JspTag parent = null;
if (!((parent = getParent()) instanceof ChooseTag)) {
throw new JspTagException("WHEN_OUTSIDE_CHOOSE");
}
if(((ChooseTag) parent).isMatched()) {
return;
}
this.getJspBody().invoke(null);
} catch (java.io.IOException ex) {
throw new JspException("Error in OtherwiseTag tag", ex);
}
}
}
定义TLD文件
<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>f</short-name>
<uri>http://openhome.cc/jstl/fake</uri>
<tag>
<name>choose</name>
<tag-class>cc.openhome.ChooseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<tag>
<name>when</name>
<tag-class>cc.openhome.WhenTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
</tag>
<tag>
<name>otherwise</name>
<tag-class>cc.openhome.OtherwiseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
示例JSP使用自定义标签:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="f" uri="http://openhome.cc/jstl/fake"%>
<jsp:useBean id="user" class="cc.openhome.User"/>
<jsp:setProperty name="user" property="*"/>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录页面</title>
</head>
<body>
<f:choose>
<f:when test="${user.valid}">
<h1>${user.name}登录成功</h1>
</f:when>
<f:otherwise>
<h1>登录失败</h1>
</f:otherwise>
</f:choose>
</body>
</html>