- 带有
Scriptlet
的 JSP
<%
List<String> errors = (List<String>) request.getAttribute("errors");
if (errors != null) {
%>
<ul style="color: rgb(255, 0, 0);">
<%
for (String error : errors) {
%>
<li><%= error %>
</li>
<%
}
%>
</ul>
<%
}
%>
- 使用
JSTL
后的 JSP
<c:if test="${requestScope.errors != null}">
<ul style="color: rgb(255, 0, 0);">
<c:forEach var="error" items="${requestScope.errors}">
<li>${error}</li>
</c:forEach>
</ul>
</c:if>
- 使用 Tag File,放在
WEB-INF/tags
目录下
<%@ tag description="显示错误信息的标签" language="java" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${requestScope.errors != null}">
<h3>请求错误</h3>
<ul style="color: rgb(255, 0, 0);">
<c:forEach var="error" items="${requestScope.errors}">
<li>${error}</li>
</c:forEach>
</ul>
</c:if>
Tag File
中可以使用JSTL
、EL
、Scriptlet
Tag File
基本上是给不会 Java 的网页设计人员使用的
<%@ taglib prefix="html" tagdir="/WEB-INF/tags" %>
<html:Errors/>
- 虽然
tagdir
属性可以指定Tag File
的位置 - 但事实上只能放在
WEB-INF/tags
的子文件夹中 - 也就是说,如果以
tagdir
属性设置 Tag File
就只能放在WEB-INF/tags
或子文件夹中
.tag
文件会被容器转译,
- 转译为
javax.servlet.jsp.tagext.SimpleTagSupport
的子类 Errors.tag
转译后生成的类源代码名称为Errors_tag.java
- 在
Tag File
中可以使用out
config
request
response
session
jspContext
- 等隐式对象
- 其中 jspContext 转译后的是
javax.servlet.jsp.JspContext
对(pageContext
的父类)
package org.apache.jsp.tag.web;
public final class Errors_tag
extends javax.servlet.jsp.tagext.SimpleTagSupport
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
// ...
public void doTag() throws javax.servlet.jsp.JspException, java.io.IOException {
javax.servlet.jsp.PageContext _jspx_page_context = (javax.servlet.jsp.PageContext)jspContext;
javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) _jspx_page_context.getRequest();
javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) _jspx_page_context.getResponse();
javax.servlet.http.HttpSession session = _jspx_page_context.getSession();
javax.servlet.ServletContext application = _jspx_page_context.getServletContext();
javax.servlet.ServletConfig config = _jspx_page_context.getServletConfig();
javax.servlet.jsp.JspWriter out = jspContext.getOut();
// ...
}
// ...
}
- 处理标签属性,需要在
.tag
文件中添加属性指令并使用
<%@ tag description="Header 内容" language="java" pageEncoding="UTF-8" %>
<%@ attribute name="title" %>
<head>
<meta charset="utf-8">
<title>${title}</title>
</head>
<html:Header title="使用 Header 的用户注册"/>
- 到目前使用的
Tag File
都是没有Body
的,实际上可以有Body
内容的
<%@ tag description="HTML 懒人标签" language="java" pageEncoding="UTF-8" %>
<%@ attribute name="title" %>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
</head>
<body>
<jsp:doBody/>
</body>
</html>
- 使用有
Body
的Tag File
的时候,默认不允许有Scriptlet
tag
指示元素的body-content
属性默认为scriptless
<%@ tag body-content="scriptless" pageEncoding="UTF-8" %>
body-content
scriptless
empty
:只能使用<html:Html/>
,在其他设置下可以有Body
和无Body
都使用tagdependent
:Scriptlet
、EL
、自定义标签都当作纯文字输出
只要是有 Body
的 Tag File
- 在其中编写
Scriptlet
就是没有意义的 - 要不就是不允许出现,要不就是当作纯文字输出
- 如果将
Tag File
的*.tag
文件放在WEB-INF/tags
文件夹或者其子文件夹 - 并在 JSP 中使用
taglib
指示元素的tagdir
属性指定*.tag
的位置 - 就可以使用
Tag File
了 - 其他人如果也想使用你的
Tag File
- 只需要将你的
*.tag
文件复制到他们自己的WEB-INF/tags
文件夹下面就可以使用了
但是,
- Java 程序员一般是将自己的代码打包为一个
*.jar
文件一起发给别人使用 - 而且,来回拷贝
*.tag
文件不是一个好办法 - 这个时候,就需要
TLD
文件,这个文件就是你的标签的一个描述文件
使用 TLD
文件注意:
*.tag
文件必须放在*.jar
文件的META-INF/tags
文件夹或者子文件夹下- 要定义
TLD
(Tag Library Description
) 文件 TLD
文件必须放在*.jar
文件的META-INF/TLDS
文件夹下
<?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
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://127.0.0.1:8080/JavaWeb_Tag_war/html</uri>
<tag-file>
<name>Errors1</name>
<path>/META-INF/tags/Errors1.tag</path>
</tag-file>
<tag-file>
<name>Errors2</name>
<path>/META-INF/tags/Errors2.tag</path>
</tag-file>
<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>
cd src
jar cvf ../html.jar *
<%@ taglib prefix="html" uri="http://127.0.0.1:8080/JavaWeb_Tag_war/html" %>
public class IfTag extends SimpleTagSupport {
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
@Override
public void doTag() throws JspException, IOException {
if (test) {
// 取得 JspFragment 调用 invoke()
getJspBody().invoke(null);
}
}
}
<?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
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://127.0.0.1:8080/JavaWeb_Tag_war/f</uri>
<tag>
<name>if</name>
<tag-class>IfTagorg.example.java_web.tag.simple_tag.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
</tag>
</taglib>
<%@ taglib prefix="f" uri="http://127.0.0.1:8080/JavaWeb_Tag_war/f" %>
<f:if test="${param.password=='123456'}">
<h1>你的秘密数据在此!</h1>
</f:if>
- 编写标签类,继承
SimpleTagSupport
类,重写doTag()
方法 - 定义
TLD
文件 - 使用
taglib
指示元素
- 所有的 JSP 自定义标签都实现了
JspTag
接口 JspTag
只是一个标示接口,本身没有定义任何方法
public interface JspTag {
}
SimpleTag
接口继承了JspTag
- 定义了
Simple Tag
开发时所需的基本行为 - 开发
Simple Tag
标签处理器时必须实现SimpleTag
接口
public interface SimpleTag extends JspTag {
void doTag() throws JspException, IOException;
void setParent(JspTag var1);
JspTag getParent();
void setJspContext(JspContext var1);
void setJspBody(JspFragment var1);
}
- 不过通常继承
SimpleTagSupport
类 SimpleTagSupport
类对SimpleTag
接口的所有方法做了基本实现- 自定义标签的类只需要重新定义感兴趣的方法(
doTag()
方法)
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) { ... }
public JspTag getParent() { ... }
public void setJspContext(JspContext pc) { ... }
protected JspContext getJspContext() { ... }
public void setJspBody(JspFragment jspBody) { ... }
protected JspFragment getJspBody() { ... }
public static final JspTag findAncestorWithClass(JspTag from, Class<?> klass) { ... }
}
Simple Tag
自定义标签的生命周期:
- 创建自定义标签处理器实例
- 调用标签处理器的
setJspContext()
方法设置pageContext
实例 - 如果是嵌套标签中的内层标签,还会调用
setParent()
方法,并传入外层标签处理器的实例 - 设置标签处理器的属性(例如
IfTag
类中的setTest()
方法设置) - 调用标签处理器的
setJspBody()
方法设置JspFragment
实例 - 嗲用标签处理器的
doTag()
方法 - 销毁标签处理器实例
- 每一次的请求都会创建新的标签处理器实例,而在执行
doTag()
后就销毁实例 - 所以
Simple Tag
的实现中,建议不要有一些耗资源的动作,如庞大的对象、连线的获取等 - 正如
Simple Tag
名称所表示的 - 这并不仅代表它实现上比较简单(相较于
Tag
的实现方式) - 也代表着它最好用来做一些简单的事务
同样的道理,
- 由于
Tag File
转译后会成为继承SimpleTagSupport
的类 - 所以在
Tag File
中,也建议不要有一些耗资源的操作
标签处理器中设置了 pageContext
- 可以用它来取得 JSP 页面的所有对象
- 进行所有在 JSP 页面
Scriptlet
中可以执行的内容
JspFragment
就如其名称所示,是个 JSP 页面中的片段内容
- 在 JSP 中使用自定义标签时若包括
Body
,将会转译为一个JspFragment
类 - 而
Body
内容将会在invoke()
方法中处理 - 在
<f:if>
例子中Body
内容将转译为以下的JspFragment
实现类(一个内部类)
private class Helper
extends org.apache.jasper.runtime.JspFragmentHelper
{
// ...
public boolean invoke0( javax.servlet.jsp.JspWriter out )
throws java.lang.Throwable
{
out.write("\n");
out.write(" <h1>你的秘密数据在此!</h1>\n");
return false;
}
public void invoke( java.io.Writer writer )
throws javax.servlet.jsp.JspException
{
// ...
if( writer != null ) {
out = this.jspContext.pushBody(writer);
} else {
out = this.jspContext.getOut();
}
try {
// ...
invoke0( out );
// ...
}
catch( java.lang.Throwable e ) {
if (e instanceof javax.servlet.jsp.SkipPageException)
throw (javax.servlet.jsp.SkipPageException) e;
throw new javax.servlet.jsp.JspException( e );
}
finally {
if( writer != null ) {
this.jspContext.popBody();
}
}
}
}
- 所以在
doTag()
方法中使用getJspBody()
取得JspFragment
实例 - 且调用其
invoke()
方法时传入null
- 这表示将使用
PageContext
取得默认的JspWriter
对象来作输出响应(而并非不作响应) - 接着进行
Body
内容的输出 - 如果
Body
内容中包括EL
或内层标签,则会先处理(在<body-content>
设置为script1ess
的情况下) - 在上面的简单范例中,只是将
<f:if>
Body
的 JSP 片段直接输出(也就是invoke0()
的执行内容)
如果调用 JspFragment
的 invoke()
时传入了一个 Writer
实例,
- 则表示要将
Body
内容的运行结果以所设置的Writer
实例输出 - 这就是处理标签的
Body
- 这个后面再讨论
如果执行 doTag()
的过程的某些条件下必须中断接下来页面的处理或输出,
- 则可以抛出
javax.servlet.jsp.SkipPageException
异常 - 这个异常对象会在 JSP 转译后的
_jspService()
中进行处理
如果抛出其他类型的异常,
- 则在
PageContext
的handlePageException()
中会看看有无设置错误处理的相关机制 - 并尝试进行页面转发和包含的动作
- 否则就封装为
ServletException
并丢给容器做默认处理,就是Http Status 500
错误
public class ForEachTag extends SimpleTagSupport {
private String var;
private Collection<String> items;
public void setVar(String var) {
this.var = var;
}
public void setItems(Collection<String> items) {
this.items = items;
}
@Override
public void doTag() throws JspException, IOException {
for (Object o : items) {
// 设置标签 Body 可用的 EL 名称
this.getJspContext().setAttribute(var, o);
// 在循环中调用 invoke 方法
this.getJspBody().invoke(null);
this.getJspContext().removeAttribute(var);
}
}
}
- 在
doTag()
方法中每调用一次invoke()
,就会执行一次Body
内容 <f:forEach>
标签的Body
内容必须执行多次是通过多次调用invoke()
完成的
<?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
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://127.0.0.1:8080/JavaWeb_Tag_war/f</uri>
<tag>
<name>forEach</name>
<tag-class>ForEachTagorg.example.java_web.tag.simple_tag.ForEachTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>var</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.util.Collection</type>
</attribute>
</tag>
</taglib>
<f:forEach items="<%=list%>" var="s">
<h1>${s}</h1>
</f:forEach>
public class ToUpperCaseTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
StringWriter writer = new StringWriter();
this.getJspBody().invoke(writer);
String upper = writer.toString().toUpperCase();
this.getJspContext().getOut().print(upper);
}
}
- 这里就是调用
JspFragment
的invoke()
时传入了一个Writer
- 标签
Body
执行的结果输出至StringWriter
对象 - 此时再调用
StringWriter
对象的toString()
取得输出的字符串结果 - 并调用
toUpperCase()
方法将结果转为大写 - 如果这个转换为大写后的字符串要输出至用户浏览器
- 则再通过
PageContext
的getOut()
取得JspWriter
对象 - 而后调用
print()
方法输出结果
<?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
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://127.0.0.1:8080/JavaWeb_Tag_war/f</uri>
<tag>
<name>toUpperCase</name>
<tag-class>ToUpperCaseTagorg.example.java_web.tag.simple_tag.ToUpperCaseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
<f:toUpperCase>
<h1>zhangsan</h1>
</f:toUpperCase>
<f:toUpperCase>
<f:forEach items="${requestScope.names}" var="s">
<h1>${s}</h1>
</f:forEach>
</f:toUpperCase>
若标签 Body
内容中还有内层标签
- 通过
getOut()
取得的就是所设置的Writer
对象pushBody()
返回的是BodyContent
对象 - (除非内层标签在调用
invoke()
时,也设置了自己的Writer
对象) - 为
JspWriter
的子类,封装了所传入的Writer
对象 - 因为
BodyContent
实例被out
引用,而运行结果都通过out
所引用的对象输出 - 所以最后
BodyContent
将会包括所有标签Body
的运行结果(包括内层标签) - 而这些结果,将再写入
BodyContent
所封装的Writer
对象 - 在
invoke()
结束前会调用pageContext
的popBody
方法 - 从堆栈中恢复原本
getOut()
所应返回的JspWriter
对象
public void invoke(java.io.Writer writer) {
JspWriter out = null;
if (writer != null) {
out = this.jspContext().pushBody(writer);
} else {
out = this.jspContext().getOut();
}
try {
// ...
} catch (java.lang.Throwable e) {
if (e instanceof javax.servlet.jsp.SkipPageException)
throw (javax.servlet.jsp.SkipPageException) e;
throw new javax.servlet.jsp.JspException(e);
} finally {
if (writer != null) {
this.jspContext.popBody();
}
}
}
- 如果调用
invoke()
时传入了Writer
对象,则标签Body
运行结果将输出至所设置的Writer
对象
public class ChooseTag extends SimpleTagSupport {
private boolean matched;
public boolean isMatched() {
return matched;
}
public void setMatched(boolean matched) {
this.matched = matched;
}
@Override
public void doTag() throws JspException, IOException {
this.getJspBody().invoke(null);
}
}
public class WhenTag extends SimpleTagSupport {
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
@Override
public void doTag() throws JspException, IOException {
JspTag parent = getParent();
if (!(parent instanceof ChooseTag)) {
throw new JspException("WhenTag 必须置于 ChooseTag 标签中");
}
ChooseTag choose = (ChooseTag) parent;
if (!choose.isMatched() && test) {
choose.setMatched(true);
this.getJspBody().invoke(null);
}
}
}
public class OtherwiseTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
JspTag parent = getParent();
if (!(parent instanceof ChooseTag)) {
throw new JspException("OtherwiseTag 必须置于 ChooseTag 标签中");
}
ChooseTag choose = (ChooseTag) parent;
if (!choose.isMatched()) {
this.getJspBody().invoke(null);
}
}
}
<?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
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://127.0.0.1:8080/JavaWeb_Tag_war/f</uri>
<tag>
<name>choose</name>
<tag-class>ChooseTagorg.example.java_web.tag.simple_tag.ChooseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<tag>
<name>when</name>
<tag-class>WhenTagorg.example.java_web.tag.simple_tag.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>OtherwiseTagorg.example.java_web.tag.simple_tag.OtherwiseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
<f:choose>
<f:when test="${param.user == 'zhangsan'}">
<h1>${param.user}登录成功!</h1>
</f:when>
<f:otherwise>
<h1>登录失败</h1>
</f:otherwise>
</f:choose>
- 如果一个标签在多个嵌套标签中,想要直接获取某个指定类型的外层标签
- 可以使用
SimpleTagSupport
的findAncestorWithClass()
静态方法
SomeTag ancester = (SomeTag) findAncestorWithClass(this, SomeTag.class);
与 Tag File
的 TLD
文件的区别
*.tld
不一定放在META-INF/TLDS
文件夹下,只要在META-INF
文件夹或子文件夹下
.jar
文件根目录下放置编译好的类(包含类的包对应的文件夹).jar
文件META-INF
文件夹或子文件夹下放置TLD
文件
cd src
jar cvf ../fake.jar *
- 使用
Simple Tag
实现自定义标签非常简单 - 所有要实现的内容都是在
doTag()
方法中进行 - 绝大多数情况下,使用
Simple Tag
能满足自定义标签的需求
然而,
Simple Tag
是从 JSP 2.0 之后才加入到标准中- 在 JSP 2.0 之前实现的自定义标签同构
Tag
接口相关类的实现来完成 - 学习
Tag
自定义标签的目的在于: - 了解和维护 JSP 2.0 之前实现出来的自定义标签(如
JSTL
) - 使用 JSP 2.0 以上的环境很少使用
Tag
自定义标签
public class IfTag extends TagSupport {
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
@Override
public int doStartTag() throws JspException {
if (test) {
return EVAL_BODY_INCLUDE;
}
return SKIP_BODY;
}
}
<?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
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>t</short-name>
<uri>http://127.0.0.1:8080/JavaWeb_Tag_war/t</uri>
<tag>
<name>if</name>
<tag-class>IfTagorg.example.java_web.tag.tag.IfTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
</tag>
</taglib>
<%@ taglib prefix="t" uri="http://127.0.0.1:8080/JavaWeb_Tag_war/t" %>
<t:if test="${param.password=='123456'}">
<h1>你的秘密数据在此!</h1>
</t:if>
public interface JspTag {
}
public interface Tag extends JspTag {
int SKIP_BODY = 0;
int EVAL_BODY_INCLUDE = 1;
int SKIP_PAGE = 5;
int EVAL_PAGE = 6;
void setPageContext(PageContext var1);
void setParent(Tag var1);
Tag getParent();
int doStartTag() throws JspException;
int doEndTag() throws JspException;
void release();
}
public interface IterationTag extends Tag {
int EVAL_BODY_AGAIN = 2;
int doAfterBody() throws JspException;
}
public class TagSupport implements IterationTag, Serializable {
private Tag parent;
private Hashtable<String, Object> values;
protected String id;
protected PageContext pageContext;
public static final Tag findAncestorWithClass(Tag from, Class klass) { ... }
public TagSupport() {}
public int doStartTag() throws JspException { ... }
public int doEndTag() throws JspException { ... }
public int doAfterBody() throws JspException { ... }
public void release() { ... }
public void setParent(Tag t) { ... }
public Tag getParent() { ... }
public void setId(String id) { ... }
public String getId() { ... }
public void setPageContext(PageContext pageContext) { ... }
public void setValue(String k, Object o) { ... }
public Object getValue(String k) { ... }
public void removeValue(String k) { ... }
public Enumeration<String> getValues() { ... }
}
当 JSP 遇到 TagSupport
自定义标签时,会进行以下动作:
- 尝试从标签池(
Tag Pool
)找到可用的标签对象,如果找到就直接使用,如果没找到就创建先的标签对象 - 调用标签处理器的
setPageContext()
方法设置PageContext
实例 - 如果时嵌套标签中的内层标签,调用标签处理器的
setParent()
方法,传入外层标签处理器的实例 - 设置标签处理器的属性
- 调用标签处理器的
doStartTag()
方法,并依据不同的返回值决定是否执行Body
或调用doAfterBody()
、doEndBody()
方法 - 将标签处理器实例置入标签池中以便再次使用
Tag
实例是可以重复使用的(Simple Tag
实例则是每次请求都创建新对象,用完就销毁回收)。
使用自定义 Tag
类,
- 要注意对象状态是否会被保留下来
- 在必要的时候,可以在
doStartTag()
方法中对对象的状态进行重置
release()
方法只会在标签实例真正被销毁回收前被调用。
public class ForEachTag extends TagSupport {
private String var;
private Iterator<?> iterator;
public void setVar(String var) {
this.var = var;
}
public void setItems(Collection<?> items) {
this.iterator = items.iterator();
}
@Override
public int doStartTag() throws JspException {
if (iterator.hasNext()) {
this.pageContext.setAttribute(var, iterator.next());
return EVAL_BODY_AGAIN;
}
return SKIP_BODY;
}
@Override
public int doAfterBody() throws JspException {
if (iterator.hasNext()) {
this.pageContext.setAttribute(var, iterator.next());
return EVAL_BODY_AGAIN;
}
this.pageContext.removeAttribute(var);
return SKIP_BODY;
}
}
<?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
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>t</short-name>
<uri>http://127.0.0.1:8080/JavaWeb_Tag_war/t</uri>
<tag>
<name>forEach</name>
<tag-class>ForEachTagorg.example.java_web.tag.tag.ForEachTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.util.Collection</type>
</attribute>
</tag>
</taglib>
<t:forEach items="${requestScope.names}" var="s">
<h1>${s}</h1>
</t:forEach>
public interface BodyTag extends IterationTag {
/** @deprecated */
int EVAL_BODY_TAG = 2;
int EVAL_BODY_BUFFERED = 2;
void setBodyContent(BodyContent var1);
void doInitBody() throws JspException;
}
public class BodyTagSupport extends TagSupport implements BodyTag {
protected BodyContent bodyContent;
public BodyTagSupport() {}
public int doStartTag() throws JspException { ... }
public int doEndTag() throws JspException { ... }
public void setBodyContent(BodyContent b) { ... }
public void doInitBody() throws JspException {}
public int doAfterBody() throws JspException { ... }
public void release() { ... }
public BodyContent getBodyContent() { ... }
public JspWriter getPreviousOut() { ... }
}
public class ChooseTag extends TagSupport {
private boolean matched;
public boolean isMatched() {
return matched;
}
public void setMatched(boolean matched) {
this.matched = matched;
}
@Override
public int doStartTag() throws JspException {
matched = false;
return EVAL_BODY_INCLUDE;
}
}
public class WhenTag extends TagSupport {
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
@Override
public int doStartTag() throws JspException {
JspTag parent = getParent();
if (!(parent instanceof ChooseTag)) {
throw new JspException("WhenTag 必须置于 ChooseTag 标签中");
}
ChooseTag choose = (ChooseTag) parent;
if (choose.isMatched() || !test) {
return SKIP_BODY;
}
choose.setMatched(true);
return EVAL_BODY_INCLUDE;
}
}
public class OtherwiseTag extends TagSupport {
@Override
public int doStartTag() throws JspException {
JspTag parent = getParent();
if (!(parent instanceof ChooseTag)) {
throw new JspException("OtherwiseTag 必须置于 ChooseTag 标签中");
}
ChooseTag choose = (ChooseTag) parent;
if (choose.isMatched()) {
return SKIP_BODY;
}
return EVAL_BODY_INCLUDE;
}
}
<?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
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>t</short-name>
<uri>http://127.0.0.1:8080/JavaWeb_Tag_war/t</uri>
<tag>
<name>choose</name>
<tag-class>ChooseTagorg.example.java_web.tag.tag.ChooseTag</tag-class>
<body-content>JSP</body-content>
</tag>
<tag>
<name>when</name>
<tag-class>WhenTagorg.example.java_web.tag.tag.WhenTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
</tag>
<tag>
<name>otherwise</name>
<tag-class>OtherwiseTagorg.example.java_web.tag.tag.OtherwiseTag</tag-class>
<body-content>JSP</body-content>
</tag>
</taglib>
<t:choose>
<t:when test="${param.user == 'zhangsan'}">
<h1>${param.user}登录成功!</h1>
</t:when>
<t:otherwise>
<h1>登录失败</h1>
</t:otherwise>
</t:choose>