JSP 自定义标签
写一个标签处理器、TLD 描述文件、在页面里使用。
Key Points自定义标签 = 一个继承 SimpleTagSupport 的类 + 一个 TLD 描述文件 + 页面里的一句 taglib 指令。
三部分
| Part | File | Job |
|---|---|---|
| Handler | XxxTag.java | 写标签被解析时做什么 |
| Descriptor | WEB-INF/mytags.tld | 声明标签名、属性、处理器类 |
| Usage | *.jsp | <%@ taglib %> + 标签 |
标签处理器
Java
package com.example.tags;
import java.io.IOException;
import jakarta.servlet.jsp.JspException;
import jakarta.servlet.jsp.tagext.SimpleTagSupport;
public class HelloTag extends SimpleTagSupport {
private String name = "World";
public void setName(String name) { this.name = name; }
@Override
public void doTag() throws JspException, IOException {
getJspContext().getOut().print("Hello, " + name + "!");
}
}SimpleTagSupport has a single method to implement, doTag(). Write output with getJspContext().getOut(); run the body with getJspBody().invoke(null).
TLD 描述文件
XML
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="3.0">
<tlib-version>1.0</tlib-version>
<short-name>my</short-name>
<uri>http://example.com/tags/my</uri>
<tag>
<name>hello</name>
<tag-class>com.example.tags.HelloTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>| Element | Meaning |
|---|---|
uri | 页面 taglib 指令里用的标识 |
name | 页面上写的标签名 |
body-content | empty / scriptless / tagdependent |
rtexprvalue | 能否用 ${...} 或 <%= %> 赋值 |
在页面里使用
JSP
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="my" uri="http://example.com/tags/my" %>
<my:hello/>
<my:hello name="${param.user}"/>Output
Hello, World!
Hello, Ada!带标签体的标签
Java
public class UpperTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
StringWriter sw = new StringWriter();
getJspBody().invoke(sw); // 先执行标签体,结果进 sw
getJspContext().getOut().print(sw.toString().toUpperCase());
}
}JSP
<my:upper>hello world</my:upper>Output
HELLO WORLDgetJspBody().invoke(sw) captures the body output into a StringWriter so you can transform it before writing. That is the standard pattern for escaping, highlighting and truncation tags.
Keep the TLD under
WEB-INF (or WEB-INF/tags, or META-INF in a jar). In the wrong place the container cannot resolve the uri and the tags are echoed as plain HTML — with no error at all, the hardest kind to debug.什么时候值得写
- 同一段展示逻辑重复三次以上 —— 比如分页条、权限按钮、状态徽章
- 需要在视图里做转义/格式化 —— 比在每个页面写脚本可靠
- 不写 —— 一次性需求、能用 JSTL 组合解决的、逻辑属于业务的
三部分
| 组成 | 文件 | 职责 |
|---|---|---|
| 处理器 | XxxTag.java | 写标签被解析时做什么 |
| 描述符 | WEB-INF/mytags.tld | 声明标签名、属性、处理器类 |
| 使用 | *.jsp | <%@ taglib %> + 标签 |
标签处理器
Java
package com.example.tags;
import java.io.IOException;
import jakarta.servlet.jsp.JspException;
import jakarta.servlet.jsp.tagext.SimpleTagSupport;
public class HelloTag extends SimpleTagSupport {
private String name = "World";
public void setName(String name) { this.name = name; }
@Override
public void doTag() throws JspException, IOException {
getJspContext().getOut().print("Hello, " + name + "!");
}
}SimpleTagSupport 只有一个方法要实现:doTag()。需要输出用 getJspContext().getOut(),需要执行标签体用 getJspBody().invoke(null)。
TLD 描述文件
XML
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="3.0">
<tlib-version>1.0</tlib-version>
<short-name>my</short-name>
<uri>http://example.com/tags/my</uri>
<tag>
<name>hello</name>
<tag-class>com.example.tags.HelloTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>| 元素 | 含义 |
|---|---|
| URI | 页面 taglib 指令里用的标识 |
| 标签名 | 页面上写的标签名 |
| 标签体 | empty / scriptless / tagdependent |
| 运行时表达式 | 能否用 ${...} 或 <%= %> 赋值 |
在页面里使用
JSP
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="my" uri="http://example.com/tags/my" %>
<my:hello/>
<my:hello name="${param.user}"/>Output
Hello, World!
Hello, Ada!带标签体的标签
Java
public class UpperTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
StringWriter sw = new StringWriter();
getJspBody().invoke(sw); // 先执行标签体,结果进 sw
getJspContext().getOut().print(sw.toString().toUpperCase());
}
}JSP
<my:upper>hello world</my:upper>Output
HELLO WORLDgetJspBody().invoke(sw) 把标签体的输出截到 StringWriter 里,处理后再写出去。这是做「转义」「高亮」「截断」类标签的标准套路。
TLD 放在
WEB-INF 下(或 WEB-INF/tags、jar 的 META-INF)。放错位置容器找不到,uri 解析失败,页面上的标签会原样输出成 HTML——而且不会报错,最难排查。什么时候值得写
- 同一段展示逻辑重复三次以上 —— 比如分页条、权限按钮、状态徽章
- 需要在视图里做转义/格式化 —— 比在每个页面写脚本可靠
- 不写 —— 一次性需求、能用 JSTL 组合解决的、逻辑属于业务的