在開發 Web 應用程式時,將表現邏輯與業務邏輯分離至關重要。Java 平台近年來在架構層面強化了這種分離,例如在 JSP 架構中引入 JavaBean 組件,讓開發人員透過 JSP 標籤存取經特殊編碼的 Java 組件屬性,由這些組件代表表現層執行後端業務處理。
JSP 自定義標籤正是 JSP/JavaBean 架構的產物。它們同樣有助於分離表現與業務邏輯,更成為網頁設計師(HTML、XML、JavaScript)與軟體工程師(Java 代碼、SQL、演算法)之間的橋梁。
JSP 自定義標籤是使用者定義的標籤,遵循 JSP JavaBean 標籤(useBean、getProperty、setProperty)所用的特殊 XML 語法。當 Servlet 容器處理自定義標籤時,會調用一個或多個 Java 類別檔案來處理,方式與處理 JSP 頁面中的 JavaBean 呼叫類似。容器會取得標籤名稱、屬性及標籤主體內容,傳遞給對應的類別進行處理。
Java 開發人員編寫標籤處理類別來處理標籤及所需的 Java 代碼和資料操作。對網頁設計師而言,自定義標籤外觀與使用方式與標準 HTML 標籤無異,但能利用後端動態資料。正確編寫的自定義標籤讓設計師無需撰寫 Java 代碼即可建立、查詢、操作資料;Java 開發人員則能專注業務邏輯,無需操心表現層。如此團隊各成員皆可專注所長。
JSP 架構實現自定義標籤需要三大組件:每頁的 JSP 宣告、web.xml 中的項目、以及包含特殊 XML 檔案(TLD)和處理類別的 JAR 檔。以下五個步驟將引導你完成整個流程:
內容淺顯易懂,讓我們立即開始。
以顯示當前日期時間的簡單自定義標籤 <abc:displayDate /> 為例。首先編寫標籤處理類別。JSP 容器執行引用自定義標籤的頁面時,會判斷每個標籤並調用相關聯的處理類別。每個處理類別需實作 JSP API 中的特定介面。標籤分兩類:可處理標籤主體內容的、與不可處理的。範例如下:
<abc:tagWithNoBody attribute="value"/>
<abc:tagWithBody attribute="value">
This is some body content that the tag handler can operate upon.
</abc:tagWithBody>
DateTag 無需主體內容,故實作 Tag 介面(通常透過繼承 TagSupport 類別)。若需處理主體,則實作 BodyTag 介面(繼承 BodyTagSupport)。以下是 DateTag 處理類別原始碼:
package myTags;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import javax.servlet.http.*;
import java.text.*;
import java.util.*;
public class DateTag extends TagSupport {
public int doStartTag() throws javax.servlet.jsp.JspException {
HttpServletRequest req;
Locale locale;
HttpJspPage g;
DateFormat df;
String date;
JSPWriter out;
req = (HttpServletRequest) pageContext.getRequest();
locale = req.getLocale();
df = SimpleDateFormat.getDateInstance(SimpleDateFormat.FULL, locale);
date = df.format(new java.util.Date());
try {
out = pageContext.getOut();
out.print(date);
} catch (IOException ioe) {
throw new JspException("I/O Error: " + ioe.getMessage());
}
return Tag.SKIP_BODY;
}
}
TagSupport 可只覆寫所需方法。本例僅實作 doStartTag(),遇到開始標籤時調用。SKIP_BODY 因標籤無主體內容。pageContext.getOut() 取得輸出緩衝區,直接寫入輸出串流。pageContext 是提供當前頁面相關屬性存取的隱含物件。編譯後將類別檔放入 Web 應用程式的 WEB-INF/classes 目錄。若有多個處理類別,可打包成 JAR 檔置於 WEB-INF/lib。
下一步定義標籤庫描述符(TLD),這是一個 XML 檔案,映射自定義標籤與處理類別。本例命名為 DateTagLib.tld(標準副檔名 .tld)。
<?xml version="1.0" encoding="ISO-8859-1"?>
<taglib>
<tlibversion>1.0</tlibversion>
<info>A simple tag library</info>
<tag>
<name>displayDate</name>
<tagclass>myTags.DateTag</tagclass>
<bodycontent>empty</bodycontent>
<info>Display Date</info>
</tag>
</taglib>
此 TLD 包含核心資訊:標籤名稱、處理類別、主體內容類型(empty 表示無主體)。較複雜情況可加入更多 XML 標籤,一個庫也常定義多個標籤。
有兩種方式:
lib 目錄。classes 子目錄,TLD 放在 WEB-INF 目錄下。本例採用第二種。步驟 1 已將類別放入 classes,只需將 TLD 存入 WEB-INF/lib 目錄(部署為 JAR 時則放在 META-INF/)。
建立 JSP 頁面與標籤庫的引用,有靜態與動態兩種宣告方式。
在 web.xml 加入:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app>
<taglib>
<taglib-uri>myTags</taglib-uri>
<taglib-location>/WEB-INF/lib/DateTagLib.tld</taglib-location>
</taglib>
</web-app>
然後在需使用的 JSP 頁面加入宣告:
<%@ taglib uri="myTags" prefix="abc" %>
uri 屬性需與 web.xml 的 taglib-uri 相符。
直接在 JSP 頁面指定 TLD 路徑:
<%@ taglib uri="/WEB-INF/lib/DateTagLib.tld" prefix="abc" %>
web.xml 查詢,修改庫位置需重啟伺服器,但提供間接層,變更屬性毋需改動頁面。完成準備工作後,即可在 JSP 中使用自定義標籤。範例頁面如下:
<%@ taglib uri="/WEB-INF/lib/DateTagLib.tld" prefix="abc" %>
<HTML>
<HEAD><TITLE>Date tag example</TITLE></HEAD>
<BODY>
<H1>Date tag Example</H1>
<p>Hi today is <b><abc:displayDate /></b> </p>
</BODY>
</HTML>
重新啟動 Web 伺服器測試,瀏覽器輸出類似下圖:

你所需的自定義標籤功能中,超過 80% 已由 J2EE 社群標準化實作於 Java 標準標籤庫(JSTL)。JSTL 由 Java Community Process 設計,參考實作由 Apache Jakarta Taglibs 專案維護。它提供變數支援、流程控制、URL 管理、XML 操作、國際化、資料庫存取等豐富標籤,並定義表達式語言(EL),輕鬆存取應用資料、操作資料而無需腳本或請求時運算式。
採用 JSTL 不僅節省從頭開發的時間,更具標準化優勢:廠商支援、豐富文檔、易尋找具經驗人才。在 J2EE Web 開發日益重視業務與表現邏輯分離的今天,JSP 自定義標籤提供比傳統 JavaBean 和 Scriptlet 更優雅的方案,而 JSTL 則是現成的標準庫。
下月我們將繼續探索 J2EE 技術,分析 Web 應用程式安全架構。祝探索愉快!