JSP 早期大量使用 Scriptlet (<% ... %>) 嵌入 Java 代碼,導致業務邏輯與視圖渲染耦合,維護困難。JSP 2.0 引入標籤庫機制,允許開發者將通用展示邏輯封裝為聲明式標籤,頁面作者僅需像使用 HTML 標籤一樣調用,實現「零 Java 代碼」的頁面開發。自定義標籤的核心優勢:
在 WEB-INF/tlds/mytags.tld 建立 TLD 檔案(XML 格式):
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="https://java.sun.com/xml/ns/javaee"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<tlib-version>1.0</tlib-version>
<short-name>my</short-name>
<uri>https://example.com/tags/mytags</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>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
關鍵元素說明:
uri:全域唯一標識,JSP 頁面透過 taglib 指令引用。body-content:empty(無體)、scriptless(無 Scriptlet,可含 EL/標籤)、tagdependent(原文處理)、JSP(任意內容)。rtexprvalue:屬性值是否支援運行時表達式(如 ${user.name})。繼承 SimpleTagSupport (JSP 2.0+ 推薦) 或 TagSupport (傳統):
package com.example.tags;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import javax.servlet.jsp.JspContext;
import java.io.IOException;
public class HelloTag extends SimpleTagSupport {
private String name = "World"; // 預設值
// 屬性 Setter(容器自動調用)
public void setName(String name) {
this.name = name;
}
@Override
public void doTag() throws IOException {
JspContext ctx = getJspContext();
// 獲取輸出流
ctx.getOut().write("Hello, " + name + "! 當前時間:" + new java.util.Date());
}
}
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="my" uri="https://example.com/tags/mytags" %>
<!DOCTYPE html>
<html>
<head><title>自定義標籤演示</title></head>
<body>
<h1>標籤調用示例</h1>
<!-- 無屬性調用,使用預設值 -->
<p><my:hello /></p>
<!-- 傳遞屬性 -->
<p><my:hello name="開發者" /></p>
<!-- 配合 EL 表達式動態傳值 -->
<p><my:hello name="${param.username}" /></p>
</body>
</html>
若 body-content 設為 scriptless,可在 doTag() 中獲取並處理體內容:
public class RepeatTag extends SimpleTagSupport {
private int times = 1;
public void setTimes(int times) { this.times = times; }
@Override
public void doTag() throws IOException, JspException {
// 捕獲體內容到 StringWriter
java.io.StringWriter sw = new java.io.StringWriter();
getJspBody().invoke(sw); // 執行體內容並寫入 sw
String body = sw.toString();
for (int i = 0; i < times; i++) {
getJspContext().getOut().write(body);
}
}
}
使用方式:
<my:repeat times="3">
<div class="item">第 ${status.index + 1} 項</div>
</my:repeat>
父標籤可透過 findAncestorWithClass() 獲取祖先標籤實例,實現協作:
<my:form>
<my:input name="username" label="用戶名" />
<my:input name="password" label="密碼" type="password" />
<my:submit value="登入" />
</my:form>
FormTag 收集子標籤資料,統一輸出 <form> 標籤與驗證腳本。
JSP 2.0+ 支援 .tag 檔案,放置於 WEB-INF/tags/ 目錄,即為標籤:
<%-- WEB-INF/tags/card.tag --%>
<%@ attribute name="title" required="true" %>
<%@ attribute name="image" required="false" %>
<div class="card">
<img src="${image}" alt="${title}">
<h3>${title}</h3>
<jsp:doBody/> <!-- 渲染標籤體 -->
</div>
使用:<my:card title="產品" image="/img/p1.jpg">描述內容</my:card>
META-INF/tlds/ 目錄。META-INF/MANIFEST.MF 添加 Taglib-URI、Taglib-Version 屬性(可選)。maven-jar-plugin 配置 <include>META-INF/tlds/**</include>。web.xml,容器自動掃描識別。doStartTag/doEndTag),支援標籤體靈活處理。javax.servlet.jsp.JspRuntimeLibrary.escapeXml() 或 JSTL <c:out>。JspContext 作用域中。<description>、<example>,生成標籤庫文檔供前端參考。自定義標籤是 JSP 實現組件化開發的關鍵技術。掌握 TLD 定義、標籤處理器實現、標籤體處理、巢狀協作、標籤檔快速開發、打包發佈全流程,能顯著提升團隊開發效率與代碼質量。雖然現代前端框架(Vue/React)已成為主流,但在存量 JSP 專案維護、純後端渲染場景、郵件模板、報表生成等領域,標籤庫技術依然具有不可替代的價值。