JSTL标签全栈实战:电商订单管理系统开发指南

108 阅读4分钟

image.png

肖哥弹架构 跟大家“弹弹” Sharding-JDBC设计与实战应用,需要代码关注

欢迎 点赞,点赞,点赞。

关注公号Solomon肖哥弹架构获取更多精彩内容

本文通过一个完整的电商订单管理系统案例,全面讲解JSTL(JSP标准标签库)五大核心标签库的实战应用,包含:

  1. 核心标签库(c:)  - 变量操作、条件分支、循环遍历
  2. 格式化标签库(fmt:)  - 日期/数字格式化、国际化支持
  3. 函数标签库(fn:)  - 字符串处理、集合操作
  4. XML标签库(x:)  - XML解析与XPath数据提取
  5. SQL标签库(sql:)  - 数据库查询(附最佳实践警告)

一、常用JSP标签用法案例

1. JSP 基本语法标签

(1) <% ... %>(脚本片段)

作用:嵌入 Java 代码片段
用法

<% 
    String name = "John";
    out.println("Hello, " + name); 
%>

输出

Hello, John

(2) <%= ... %>(表达式输出)

作用:直接输出变量或表达式结果
用法

<%= "Current time: " + new java.util.Date() %>

输出

Current time: Wed Jun 26 14:30:45 CST 2024

(3) <%! ... %>(声明标签)

作用:声明全局变量或方法
用法

<%!
    int count = 0;
    public void increment() {
        count++;
    }
%>
<%
    increment();
    out.print("Count: " + count);
%>

输出

Count: 1

2. JSP 指令标签

(1) <%@ page %>(页面指令)

作用:定义页面属性(编码、导入类等)
参数说明

参数说明
language="java"脚本语言(默认Java)
contentType="text/html;charset=UTF-8"响应内容类型
import="java.util.*"导入Java包
session="true"是否启用session
errorPage="error.jsp"指定错误处理页面
用法
<%@ page language="java" contentType="text/html; charset=UTF-8" 
    import="java.util.Date" session="true" %>

输出:无直接输出,影响页面行为。

(2) <%@ include %>(静态包含)

作用:在编译时合并另一个文件内容
用法

<%@ include file="header.jsp" %>

输出
header.jsp的内容插入当前页面。

(3) <%@ taglib %>(标签库引入)

作用:引入JSTL或自定义标签库
参数说明

参数说明
uri="http://java.sun.com/jsp/jstl/core"标签库URI
prefix="c"标签前缀
用法
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

输出:无直接输出,启用JSTL标签功能。

3. JSP 动作标签

(1) <jsp:include>(动态包含)

作用:运行时包含另一个页面
参数说明

参数说明
page="footer.jsp"目标页面路径
用法
<jsp:include page="footer.jsp" />

输出
动态加载footer.jsp的内容。

(2) <jsp:forward>(请求转发)

作用:跳转到另一个页面
参数说明

参数说明
page="target.jsp"目标页面路径
用法
<jsp:forward page="target.jsp" />

输出
终止当前页面,跳转到target.jsp

(3) <jsp:useBean>(JavaBean操作)

作用:创建或访问JavaBean对象
参数说明

参数说明
id="user"Bean变量名
class="com.example.User"Bean类全限定名
scope="session"作用域(page/request/session/application)
用法
<jsp:useBean id="user" class="com.example.User" scope="session" />

输出
在session中创建或获取User对象。

4. JSTL 核心标签

(1) <c:out>(安全输出)

作用:避免XSS攻击,输出转义内容
参数说明

参数说明
value="${data}"要输出的值
default="N/A"默认值(可选)
用法
<c:out value="${user.name}" default="Guest" />

输出
user.name存在则输出,否则输出Guest

(2) <c:if>(条件判断)

作用:简单条件分支
参数说明

参数说明
test="${salary > 2000}"布尔表达式
用法
<c:if test="${salary > 2000}">
    <p>High salary!</p>
</c:if>

输出
若条件为真,输出<p>High salary!</p>

(3) <c:forEach>(循环遍历)

作用:遍历集合或数组
参数说明

参数说明
items="${users}"要遍历的集合
var="user"当前项变量名
varStatus="status"循环状态对象(可选)
用法
<c:forEach items="${users}" var="user" varStatus="status">
    ${status.index}: ${user.name}<br>
</c:forEach>

输出

0: Alice<br>
1: Bob<br>
2: Charlie<br>

5. 自定义标签开发

(1) 定义标签处理器

public class HelloTag extends SimpleTagSupport {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void doTag() throws JspException, IOException {
        getJspContext().getOut().write("Hello, " + name + "!");
    }
}

(2) TLD配置

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

(3) JSP中使用

<%@ taglib uri="/WEB-INF/tags.tld" prefix="mytag" %>
<mytag:hello name="World" />

输出

Hello, World!

二、JSTL 完整标签库参考手册

1. JSTL Core 核心标签库 (c:)

(1) 表达式控制标签

标签作用语法参数说明示例
<c:out>输出表达式值<c:out value="expr" [default="def"] [escapeXml="true/false"]/>value: 要输出的表达式 default: 默认值 escapeXml: 是否转义XML<c:out value="${user.name}" default="Guest"/>
<c:set>设置变量值<c:set var="name" value="expr" [scope="page/request/session/application"]/>var: 变量名 value: 值 scope: 作用域<c:set var="admin" value="${user.role == 'admin'}" scope="session"/>
<c:remove>移除变量<c:remove var="name" [scope="page/request/session/application"]/>var: 变量名 scope: 作用域<c:remove var="tempData" scope="request"/>

(2) 流程控制标签

标签作用语法参数说明示例
<c:if>条件判断<c:if test="condition">content</c:if>test: 布尔表达式<c:if test="${empty cart}">购物车为空</c:if>
<c:choose>多条件选择<c:choose><c:when test="...">...</c:when><c:otherwise>...</c:otherwise></c:choose>test: 条件表达式见下方完整示例
<c:when>choose的子条件<c:when test="condition">content</c:when>test: 布尔表达式
<c:otherwise>choose的默认分支<c:otherwise>content</c:otherwise>

完整示例:

<c:choose>
    <c:when test="${score >= 90}">优秀</c:when>
    <c:when test="${score >= 60}">及格</c:when>
    <c:otherwise>不及格</c:otherwise>
</c:choose>

(3) 循环迭代标签

标签作用语法参数说明示例
<c:forEach>集合遍历<c:forEach items="collection" [var="varName"] [varStatus="status"] [begin="start"] [end="end"] [step="step"]>content</c:forEach>items: 迭代集合 var: 当前项变量名 varStatus: 状态对象 begin/end/step: 控制子集<c:forEach items="${users}" var="u" varStatus="s">${s.index}: ${u.name}</c:forEach>
<c:forTokens>字符串分割<c:forTokens items="string" delims="delimiters" [var="varName"] [varStatus="status"]>content</c:forTokens>items: 要分割的字符串 delims: 分隔符 var: 当前项变量名<c:forTokens items="apple,orange,banana" delims="," var="fruit">${fruit}</c:forTokens>

(4) URL相关标签

标签作用语法参数说明示例
<c:url>URL编码<c:url value="url" [var="varName"] [scope="scope"] [context="contextPath"]/>value: 基础URL var: 存储变量名 context: 上下文路径<c:url value="/product?id=${p.id}" var="productUrl"/>
<c:param>添加URL参数<c:param name="paramName" value="paramValue"/>name: 参数名 value: 参数值<c:url value="/search"><c:param name="keyword" value="${kw}"/></c:url>
<c:redirect>重定向<c:redirect url="url" [context="contextPath"]/>url: 目标URL context: 上下文路径<c:redirect url="/login.jsp"/>

2. JSTL Functions 函数库 (fn:)

需额外声明:

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
函数作用语法示例
fn:contains()判断包含${fn:contains(string, substring)}${fn:contains(name, "Admin")}
fn:escapeXml()XML转义${fn:escapeXml(unsafeString)}${fn:escapeXml(userInput)}
fn:join()数组连接${fn:join(array, delimiter)}${fn:join(tags, ",")}
fn:length()获取长度${fn:length(collection/string)}${fn:length(users)}
fn:split()字符串分割${fn:split(string, delimiter)}${fn:split(csvData, ",")}
fn:startsWith()判断前缀${fn:startsWith(string, prefix)}${fn:startsWith(url, "http")}
fn:substring()获取子串${fn:substring(string, begin, end)}${fn:substring(title, 0, 20)}
fn:toLowerCase()转小写${fn:toLowerCase(string)}${fn:toLowerCase(username)}
fn:toUpperCase()转大写${fn:toUpperCase(string)}${fn:toUpperCase(status)}
fn:trim()去除空格${fn:trim(string)}${fn:trim(input)}

3. JSTL Formatting 格式化标签库 (fmt:)

需额外声明:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

(1) 数字格式化

标签作用语法示例
<fmt:formatNumber>数字格式化<fmt:formatNumber value="num" [type="number/currency/percent"] [pattern="customPattern"]/><fmt:formatNumber value="${price}" type="currency"/>
<fmt:parseNumber>字符串转数字<fmt:parseNumber value="string" [type="number/currency/percent"]/><fmt:parseNumber value="${strNum}"/>

(2) 日期格式化

标签作用语法示例
<fmt:formatDate>日期格式化<fmt:formatDate value="date" [type="date/time/both"] [pattern="yyyy-MM-dd"]/><fmt:formatDate value="${now}" pattern="yyyy-MM-dd HH:mm"/>
<fmt:parseDate>字符串转日期<fmt:parseDate value="string" [pattern="yyyy-MM-dd"]/><fmt:parseDate value="${strDate}" pattern="MM/dd/yyyy"/>

(3) 国际化支持

标签作用语法示例
<fmt:setLocale>设置地区<fmt:setLocale value="zh_CN" [variant="..."] [scope="page/request/session/application"]/><fmt:setLocale value="en_US"/>
<fmt:setBundle>设置资源包<fmt:setBundle basename="messages" [var="varName"] [scope="scope"]/><fmt:setBundle basename="com/myapp/messages"/>
<fmt:message>获取国际化消息<fmt:message key="msgKey" [bundle="resourceBundle"]/><fmt:message key="welcome.message"/>
<fmt:requestEncoding>设置请求编码<fmt:requestEncoding value="UTF-8"/><fmt:requestEncoding value="GBK"/>

4. JSTL XML 处理标签库 (x:)

需额外声明:

<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
标签作用语法示例
<x:parse>解析XML<x:parse xml="xmlContent" [var="varName"] [scope="scope"]/><x:parse xml="${rssFeed}" var="rssDoc"/>
<x:out>输出XPath结果<x:out select="xpathExpr"/><x:out select="$rssDoc/channel/title"/>
<x:set>保存XPath结果<x:set select="xpathExpr" var="varName"/><x:set select="$doc//item" var="newsItems"/>
<x:if>XPath条件判断<x:if select="xpathExpr">content</x:if><x:if select="$product/stock > 0">有货</x:if>
<x:forEach>XPath循环<x:forEach select="xpathExpr" [var="varName"]>content</x:forEach><x:forEach select="$rssDoc//item" var="item">${item.title}</x:forEach>
<x:choose>XPath多条件类似核心库的choose
<x:transform>XSLT转换<x:transform xml="xmlDoc" xslt="xslDoc"/><x:transform xml="${xmlData}" xslt="${xslTemplate}"/>
<x:param>XSLT参数<x:param name="paramName" value="paramValue"/>用于x:transform内部

5. JSTL SQL 标签库 (sql:)

注意:实际开发中不推荐在JSP中使用SQL标签,应使用DAO层

需额外声明:

<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
标签作用语法示例
<sql:setDataSource>设置数据源<sql:setDataSource url="jdbcUrl" driver="driverClass" user="username" password="pwd" [var="varName"]/><sql:setDataSource var="db" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/test" user="root" password="123456"/>
<sql:query>执行查询<sql:query dataSource="ds" sql="sql" [var="result"]/><sql:query var="users" dataSource="${db}" sql="SELECT * FROM users"/>
<sql:update>执行更新<sql:update dataSource="ds" sql="sql" [var="count"]/><sql:update dataSource="${db}" sql="INSERT INTO users(name) VALUES('Tom')"/>
<sql:param>设置参数<sql:param value="paramValue"/><sql:query sql="SELECT * FROM products WHERE id=?"><sql:param value="${pid}"/></sql:query>
<sql:dateParam>日期参数<sql:dateParam value="dateValue" type="date/time/timestamp"/><sql:dateParam value="${orderDate}" type="timestamp"/>
<sql:transaction>事务管理<sql:transaction dataSource="ds">多个sql操作</sql:transaction>

三、电商订单管理系统 - JSTL 完整标签综合案例

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>

<!-- 1. 使用JSP脚本设置基础数据 -->
<% 
    // 模拟从数据库获取的订单数据
    java.util.Map<String, Object> order = new java.util.HashMap<>();
    order.put("orderId", "ORD202306001");
    order.put("createTime", new java.util.Date());
    order.put("totalAmount", 2999.99);
    order.put("status", "SHIPPED");
    
    // 订单商品列表
    java.util.List<java.util.Map<String, Object>> items = new java.util.ArrayList<>();
    java.util.Map<String, Object> item1 = new java.util.HashMap<>();
    item1.put("productId", "P1001");
    item1.put("productName", "智能手机 X1");
    item1.put("price", 1999.99);
    item1.put("quantity", 1);
    items.add(item1);
    
    java.util.Map<String, Object> item2 = new java.util.HashMap<>();
    item2.put("productId", "P2002");
    item2.put("productName", "无线耳机 Pro");
    item2.put("price", 499.99);
    item2.put("quantity", 2);
    items.add(item2);
    
    request.setAttribute("order", order);
    request.setAttribute("items", items);
    
    // 模拟国际化资源
    java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("messages", request.getLocale());
    request.setAttribute("bundle", bundle);
%>

<!-- 2. 核心标签库使用 -->
<!DOCTYPE html>
<html>
<head>
    <title><fmt:message key="order.detail.title"/></title>
</head>
<body>
    <!-- 2.1 变量操作 -->
    <c:set var="discount" value="0.1" scope="page"/> <!-- 设置10%折扣 -->
    
    <!-- 2.2 输出订单基本信息 -->
    <h1>
        <fmt:message key="order.detail.header"/>: 
        <c:out value="${order.orderId}" default="N/A"/> <!-- 输出结果: ORD202306001 -->
    </h1>
    
    <p>
        <fmt:message key="order.date"/>: 
        <fmt:formatDate value="${order.createTime}" pattern="yyyy-MM-dd HH:mm:ss"/> 
        <!-- 输出结果: 2023-06-15 14:30:45 -->
    </p>
    
    <!-- 2.3 条件判断 -->
    <c:choose>
        <c:when test="${order.status == 'PAID'}">
            <p class="status paid"><fmt:message key="order.status.paid"/></p>
            <!-- 当status=PAID时输出: 已支付 -->
        </c:when>
        <c:when test="${order.status == 'SHIPPED'}">
            <p class="status shipped"><fmt:message key="order.status.shipped"/></p>
            <!-- 当status=SHIPPED时输出: 已发货 -->
        </c:when>
        <c:otherwise>
            <p class="status other"><fmt:message key="order.status.other"/></p>
        </c:otherwise>
    </c:choose>
    
    <!-- 2.4 循环展示商品 -->
    <h2><fmt:message key="order.items.title"/></h2>
    <table border="1">
        <tr>
            <th><fmt:message key="order.item.id"/></th>
            <th><fmt:message key="order.item.name"/></th>
            <th><fmt:message key="order.item.price"/></th>
            <th><fmt:message key="order.item.quantity"/></th>
            <th><fmt:message key="order.item.subtotal"/></th>
        </tr>
        
        <c:forEach items="${items}" var="item" varStatus="loop">
            <!-- 输出结果: 循环输出所有商品行 -->
            <tr>
                <td>${item.productId}</td> <!-- 例如: P1001 -->
                <td>
                    <c:out value="${item.productName}"/> <!-- 例如: 智能手机 X1 -->
                </td>
                <td>
                    <fmt:formatNumber value="${item.price}" type="currency"/> 
                    <!-- 例如: ¥1,999.99 -->
                </td>
                <td>${item.quantity}</td> <!-- 例如: 1 -->
                <td>
                    <fmt:formatNumber value="${item.price * item.quantity}" type="currency"/>
                    <!-- 例如: ¥1,999.99 -->
                </td>
            </tr>
        </c:forEach>
    </table>
    
    <!-- 2.5 计算总金额 -->
    <c:set var="subtotal" value="0"/>
    <c:forEach items="${items}" var="item">
        <c:set var="subtotal" value="${subtotal + (item.price * item.quantity)}"/>
    </c:forEach>
    
    <h3>
        <fmt:message key="order.subtotal"/>: 
        <fmt:formatNumber value="${subtotal}" type="currency"/> 
        <!-- 输出结果: ¥2,999.97 -->
    </h3>
    
    <h3>
        <fmt:message key="order.discount"/>: 
        <fmt:formatNumber value="${discount}" type="percent"/> 
        <!-- 输出结果: 10% -->
    </h3>
    
    <h2>
        <fmt:message key="order.total"/>: 
        <fmt:formatNumber value="${subtotal * (1 - discount)}" type="currency"/>
        <!-- 输出结果: ¥2,699.97 -->
    </h2>
    
    <!-- 3. 函数标签库使用 -->
    <c:set var="deliveryInfo" value="北京市,朝阳区,建国路88号"/>
    <p>
        <fmt:message key="order.delivery.address"/>: 
        ${fn:replace(deliveryInfo, ',', ' ')} 
        <!-- 输出结果: 北京市 朝阳区 建国路88号 -->
    </p>
    
    <!-- 4. XML标签库演示 (模拟从API获取的XML数据) -->
    <c:set var="trackingXml">
        <tracking>
            <log>
                <time>2023-06-15T08:00:00</time>
                <location>北京转运中心</location>
                <status>已揽收</status>
            </log>
            <log>
                <time>2023-06-15T14:30:00</time>
                <location>上海分拨中心</location>
                <status>运输中</status>
            </log>
        </tracking>
    </c:set>
    
    <x:parse xml="${trackingXml}" var="trackingData"/>
    
    <h2><fmt:message key="order.tracking.title"/></h2>
    <ul>
        <x:forEach select="$trackingData/tracking/log">
            <li>
                <x:out select="time"/> - 
                <x:out select="location"/> - 
                <x:out select="status"/>
                <!-- 输出结果: 
                     2023-06-15T08:00:00 - 北京转运中心 - 已揽收
                     2023-06-15T14:30:00 - 上海分拨中心 - 运输中
                -->
            </li>
        </x:forEach>
    </ul>
    
    <!-- 5. SQL标签库演示 (仅作示例,实际项目不建议在JSP中使用) -->
    <sql:setDataSource var="db" 
        driver="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/ecommerce"
        user="root" 
        password="password"/>
    
    <sql:query var="relatedProducts" dataSource="${db}">
        SELECT * FROM products WHERE category = 'electronics' LIMIT 3
    </sql:query>
    
    <h2><fmt:message key="order.related.products"/></h2>
    <ul>
        <c:forEach var="product" items="${relatedProducts.rows}">
            <li>${product.name} - <fmt:formatNumber value="${product.price}" type="currency"/></li>
            <!-- 输出结果: 根据数据库查询结果显示3个电子产品 -->
        </c:forEach>
    </ul>
    
    <!-- 6. URL和重定向操作 -->
    <c:url var="orderListUrl" value="/orders">
        <c:param name="status" value="completed"/>
        <c:param name="page" value="1"/>
    </c:url>
    
    <p>
        <a href="${orderListUrl}">
            <fmt:message key="order.back.to.list"/>
            <!-- 生成的URL: /orders?status=completed&page=1 -->
        </a>
    </p>
    
    <!-- 7. 国际化消息 -->
    <fmt:setLocale value="${pageContext.request.locale}"/>
    <fmt:setBundle basename="messages" var="lang"/>
    
    <footer>
        <p><fmt:message key="footer.copyright"/></p>
        <!-- 输出结果: 根据语言环境显示如"© 2023 电商平台" -->
    </footer>
</body>
</html>