首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Web JSTL 標簽庫、自定義 EL 函數與標簽開發全棧指南

     編輯:Java 相關知識

前言

JSTL (JSP Standard Tag Library) 是 Java Web 開發中替代 Scriptlet 的標準方案,配合 EL 表達式實現視圖層邏輯與展示分離。本文系統講解 JSTL 核心標簽庫、EL 函數庫、自定義 EL 函數、自定義標簽 (SimpleTag) 開發全流程。

JSTL 核心標簽庫

依賴與引入

<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="https://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="https://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="sql" uri="https://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="x" uri="https://java.sun.com/jsp/jstl/xml" %>

核心標簽速查表

標簽功能關鍵屬性
<c:out>輸出值,支持 HTML 轉義value, default, escapeXml
<c:set>設置變量/屬性var, value, scope, target, property
<c:remove>移除變量var, scope
<c:catch>捕獲異常var (異常對象)
<c:if>條件判斷test, var, scope
<c:choose> <c:when> <c:otherwise>多條件分支test
<c:forEach>集合/數組/範圍迭代items, var, begin, end, step, varStatus
<c:forTokens>字符串分割迭代items, delims, var
<c:import>包含頁面/遠程資源url, context, var, scope
<c:url>構建 URL (支持會話追蹤)value, var, scope, context
<c:redirect>重定向url, context
<c:param>URL 參數 (自動編碼)name, value

varStatus 迭代狀態對象

<c:forEach items="${list}" var="item" varStatus="status">
    索引: ${status.index} (從0開始)
    計數: ${status.count} (從1開始)
    首項: ${status.first}
    末項: ${status.last}
    當前對象: ${item}
</c:forEach>

fmt 國際化格式化

<fmt:setLocale value="zh_CN" scope="session"/>
<fmt:setBundle basename="messages" var="msg"/>
<fmt:message key="welcome" bundle="${msg}"/>
<fmt:formatNumber value="12345.6" type="currency" currencySymbol="¥"/>
<fmt:formatDate value="${now}" pattern="yyyy-MM-dd HH:mm:ss"/>

EL 函數庫 (fn:) 常用函數速查

函數說明示例
toLowerCase(s)轉小寫${fn:toLowerCase('ABC')} → abc
toUpperCase(s)轉大寫${fn:toUpperCase('abc')} → ABC
trim(s)去首尾空格${fn:trim(' hi ')} → hi
length(obj)集合/數組/字符串長度${fn:length(list)} → 5
split(s, delim)分割字符串為數組${fn:split('a,b,c', ',')[1]} → b
join(arr, delim)數組連接為字符串${fn:join(arr, '-')} → a-b-c
indexOf(s, sub)子串首次索引${fn:indexOf('hello', 'el')} → 1
contains(s, sub)包含判斷 (大小寫敏感)${fn:contains('Hello', 'ell')} → true
containsIgnoreCase(s, sub)忽略大小寫包含${fn:containsIgnoreCase('Hello', 'ELL')} → true
startsWith(s, prefix)前綴判斷${fn:startsWith('hello', 'he')} → true
endsWith(s, suffix)後綴判斷${fn:endsWith('hello', 'lo')} → true
replace(s, before, after)替換${fn:replace('a b c', ' ', '-')} → a-b-c
substring(s, begin, end)截取子串${fn:substring('hello', 1, 4)} → ell
substringAfter(s, sub)子串後內容${fn:substringAfter('a.b.c', '.')} → b.c
substringBefore(s, sub)子串前內容${fn:substringBefore('a.b.c', '.')} → a
escapeXml(s)XML/HTML 轉義${fn:escapeXml('
')} → <div>

自定義 EL 函數開發

1. 靜態方法類

package com.example.el;

public class CustomFunctions {
    public static String formatMoney(double amount) {
        return String.format("¥%,.2f", amount);
    }
    
    public static String maskPhone(String phone) {
        if (phone == null || phone.length() < 7) return phone;
        return phone.substring(0, 3) + "****" + phone.substring(7);
    }
}

2. TLD 配置文件 (/WEB-INF/tld/custom-fn.tld)

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" 
    xmlns="https://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://java.sun.com/xml/ns/j2ee 
    https://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
    
    <tlib-version>1.0</tlib-version>
    <short-name>customFn</short-name>
    <uri>https://example.com/custom-fn</uri>
    
    <function>
        <name>formatMoney</name>
        <function-class>com.example.el.CustomFunctions</function-class>
        <function-signature>java.lang.String formatMoney(double)</function-signature>
    </function>
    
    <function>
        <name>maskPhone</name>
        <function-class>com.example.el.CustomFunctions</function-class>
        <function-signature>java.lang.String maskPhone(java.lang.String)</function-signature>
    </function>
</taglib>

3. JSP 中使用

<%@ taglib prefix="myfn" uri="https://example.com/custom-fn" %>
價格: ${myfn:formatMoney(12345.6)} <!-- ¥12,345.60 -->
電話: ${myfn:maskPhone('13812345678')} <!-- 138****5678 -->

自定義標簽開發

SimpleTag 支持類體系

SimpleTag (介面)
  ↑
SimpleTagSupport (抽象基類, 推薦繼承)
  ↑
TagAdapter (適配舊式 Tag 介面)

核心生命週期方法

  1. setJspContext(JspContext):注入 pageContext
  2. setParent(Tag):設置父標簽
  3. setJspBody(JspFragment):注入標簽體片段
  4. doTag():核心邏輯入口,此處輸出、迭代、操作標簽體

JspFragment 關鍵方法

// 執行標簽體內容,輸出到指定 Writer
void invoke(Writer out) throws JspException, IOException;

// 獲取調用頁面的 JspContext (即 pageContext)
JspContext getJspContext();

標簽體內容類型 (body-content)

類型說明SimpleTag 支持
empty無標簽體✅
scriptless無 Scriptlet,支持 EL/JSTL/HTML✅ (推薦)
tagdependent原文本,容器不解析,由標簽類自行處理✅
JSP完整 JSP 語法 (含 Scriptlet)❌ 不支持

三類標簽完整實現

1. 簡單輸出標簽 (無屬性、無標簽體)

public class HelloTag extends SimpleTagSupport {
    private JspContext jspContext;

    @Override
    public void setJspContext(JspContext ctx) {
        this.jspContext = ctx;
    }

    @Override
    public void doTag() throws JspException, IOException {
        jspContext.getOut().write("Hello Custom Tag!");
    }
}

2. 處理標簽體標簽 (轉大寫輸出)

public class UpperTag extends SimpleTagSupport {
    private JspContext jspContext;

    @Override
    public void setJspContext(JspContext ctx) { this.jspContext = ctx; }

    @Override
    public void doTag() throws JspException, IOException {
        JspFragment body = getJspBody();
        if (body != null) {
            StringWriter sw = new StringWriter();
            body.invoke(sw); // 捕獲標簽體輸出
            String content = sw.toString().toUpperCase();
            jspContext.getOut().write(content);
        }
    }
}

3. 帶屬性條件標簽 (模擬 c:if)

public class IfTag extends SimpleTagSupport {
    private boolean test;
    private JspContext jspContext;

    public void setTest(boolean test) { this.test = test; } // 屬性 setter

    @Override
    public void setJspContext(JspContext ctx) { this.jspContext = ctx; }

    @Override
    public void doTag() throws JspException, IOException {
        if (test) {
            getJspBody().invoke(null); // 直接輸出到頁面
        }
    }
}

TLD 完整配置 (/WEB-INF/tld/custom-tag.tld)

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" ...>
    <tlib-version>1.0</tlib-version>
    <short-name>mytag</short-name>
    <uri>https://example.com/custom-tag</uri>

    <tag>
        <name>hello</name>
        <tag-class>com.example.tag.HelloTag</tag-class>
        <body-content>empty</body-content>
    </tag>

    <tag>
        <name>upper</name>
        <tag-class>com.example.tag.UpperTag</tag-class>
        <body-content>scriptless</body-content>
    </tag>

    <tag>
        <name>if</name>
        <tag-class>com.example.tag.IfTag</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>test</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue> <!-- 支持 EL 表達式 -->
            <type>boolean</type>
        </attribute>
    </tag>
</taglib>

JSP 綜合使用示例

<%@ taglib prefix="my" uri="https://example.com/custom-tag" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>

<my:hello/> <!-- Hello Custom Tag! -->

<my:upper>
    這段內容將被轉為大寫輸出
</my:upper>

<c:set var="score" value="85"/>
<my:if test="${score >= 60}">
    恭喜通過!
</my:if>

標簽開發最佳實踐

  1. TLD 放置位置:/WEB-INF/tld/ 或子目錄 (不在 classes/lib 下)
  2. URI 唯一性:使用公司域名反轉 https://com.example/project/taglib
  3. 屬性類型:基本類型自動轉換,複雜類型需 PropertyEditor
  4. 異常處理:doTag 拋出 SkipPageException 可終止頁面後續執行
  5. 標簽巢狀:getParent() 獲取父標簽,協作共享狀態
  6. 性能:無狀態標簽類可單例,有狀態需原型作用域

總結

JSTL + EL + 自定義標簽構成了 JSP 視圖層的「三板斧」:

  • JSTL:標準化通用邏輯 (迭代、判斷、URL、國際化)
  • EL 函數:表達式級工具方法 (字符串、集合、格式化)
  • 自定義標簽:組件化復雜 UI 邏輯 (條件渲染、布局、片段復用)

掌握 SimpleTagSupport 生命週期、JspFragment.invoke() 標簽體控制、body-content 類型選擇、TLD 屬性聲明,即可開發出專業級標簽庫,徹底消除 JSP 中的 Java 代碼,實現真正的視圖分離。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved