【JavaWeb学习 | 第20篇】EL表达式与JSTL标签 - 实践


EL 表达式与 JSTL 标签详解
在 JSP 开发中,直接嵌入 Java 小脚本会导致页面结构混乱、维护困难,而 EL 表达式与 JSTL 标签的组合,能完美解决这一问题。它们不仅让页面代码更简洁,还实现了业务逻辑与视图的分离。
一、Servlet回顾
在学习新内容前,先巩固几个关键前置知识点,帮助更好理解后续内容:
- Servlet 是接收请求并做出响应的程序,开发方式包括实现 Servlet 接口或继承 HttpServlet,可通过注解或 XML 配置映射路径。
- Servlet 生命周期分为实例化、初始化(init)、处理请求(service)、销毁(destroy)四个阶段,随容器启动而创建,随容器关闭而销毁。
- 转发通过 RequestDispatcher 的 forward 方法实现,重定向通过 response 的 sendRedirect 方法实现,二者核心区别在于请求域数据是否共享。
- 过滤器可拦截请求与响应,多个过滤器组成过滤器链,开发需实现 Filter 接口或继承 HttpFilter,通过 XML 或注解配置生效。
二、EL 表达式(简化数据访问)
1. 什么是 EL 表达式
EL(Expression Language)即表达式语言,是 JSP 内置的简化数据访问的语言,无需编写复杂 Java 代码即可获取域对象中的数据。
2. 为什么要用 EL 表达式
直接使用 Java 小脚本存在诸多弊端:
- 代码结构混乱,Java 代码与 HTML 标签混合交织;
- 数据取值需手动实例化对象、强制类型转换,易出错;
- 代码可读性差,后期维护成本高。
EL 表达式可替代小脚本,实现数据自动转型,让页面代码更简洁清晰。
3. EL 表达式核心用法
(1)获取变量值
语法:${变量名}
- 无需指定域对象时,EL 会按 pageScope → requestScope → sessionScope → applicationScope 的顺序自动查找;
- 若多个域对象存在同名变量,优先取作用范围更小的(如 request 优先于 session)。
示例:
// Servlet 中存入数据
request.setAttribute("username", "test");
session.setAttribute("username", "admin");
JSP 页面取值:
当前用户:${username}
当前管理员:${sessionScope.username}
(2)EL 隐式对象
通过隐式对象可直接指定取值范围,避免同名变量冲突,常用隐式对象如下:

(3)获取复杂数据
- JavaBean 属性:
${对象名.属性名}或${对象名["属性名"]}
示例:${user.id}、${user["username"]} - List 集合:
${集合名[下标]}(下标从 0 开始)
示例:${userList[0].username}(获取集合第一个用户的用户名) - Map 集合:
${集合名.键名}或${集合名["键名"]}
示例:${scoreMap.math}、${scoreMap["english"]}
(4)EL 操作符
EL 支持关系运算、逻辑运算和空值判断,语法简洁直观:
三、JSTL 标签(解决页面逻辑处理)
1. 什么是 JSTL
JSTL(JavaServer Pages Standard Tag Library)即 JSP 标准标签库,是一套基于 XML 的标签集合,弥补了 EL 无法进行逻辑判断和循环的不足,常与 EL 配合使用。
2. JSTL 标签库分类
JSTL 包含多个功能标签库,开发中最常用核心标签库和格式化标签库:
| 标签库名称 | 资源标识符(uri) | 前缀(prefix) | 核心用途 |
|---|---|---|---|
| 核心标签库 | http://java.sun.com/jsp/jstl/core | c | 逻辑判断、循环、变量操作 |
| 国际化/格式化标签库 | http://java.sun.com/jsp/jstl/fmt | fmt | 日期、数字格式化 |
| XML 标签库 | http://java.sun.com/jsp/jstl/xml | x | XML 数据处理(较少用) |
| 数据库标签库 | http://java.sun.com/jsp/jstl/sql | sql | 数据库操作(较少用) |
| 函数标签库 | http://java.sun.com/jsp/jstl/functions | fn | 字符串处理函数 |
3. JSTL 使用前提
- 引入依赖jar包:jstl.jar 和 standard.jar
- JSP 页面顶部声明标签库,示例:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
4. 核心标签库常用标签
(1)通用标签:set、out、remove

1. <c:out>
用于输出数据到页面,类似 <%= %>,但更安全
value:要输出的表达式(EL 表达式)default:当 value 为null时显示的默认值
2. <c:set>
用于在作用域(page、request、session、application)中设置变量。
var:变量名value:变量值(可直接写或用 EL 表达式)scope:作用域(默认 page)
3. <c:remove>
var:要移除的变量的名称scope:指定要在哪个作用域中移除变量。如果不指定,它会依次在所有作用域(page, request, session, application)中查找该变量并移除(可选属性)
(2)语法标签:if、choose、forEach
1. <c:if>
条件判断标签,满足条件时执行标签体内容。
您已成年,可以访问该内容。
test:条件表达式(返回 boolean)
2. <c:choose> / <c:when> / <c:otherwise>
多条件判断,类似 Java 的 if-else if-else。
优秀
良好
加油
3. 循环标签:forEach
遍历集合或数组,是最常用的 JSTL 标签:
用户名:${user.username}
科目:${score.key},分数:${score.value}
items:待遍历的集合/数组;var:每次遍历的元素别名;begin:遍历起始下标(默认 0);end:遍历结束下标;step:遍历步长(默认 1)。
(3)格式化标签库常用标签

(1)fmt:formatDate:日期格式化
<% request.setAttribute("now", new Date()); %>
当前日期:
当前时间:
运行结果:
(2)fmt:formatNumber:数字格式化
支持货币、百分比、自定义格式:
<% request.setAttribute("money", 12345.678); %>
货币:
数字:
百分比:
运行结果:
四、案例:EL + JSTL 综合应用
下面通过一个用户列表展示案例,看如何结合 EL 和 JSTL 开发页面:
index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
首页
显示成绩
ScoreServlet文件
package com.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@WebServlet("/showScore")
public class ScoreServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().setAttribute("zhangsan",new Score("张三",90));
List<Score> scores = Arrays.asList(
new Score("林七夜",90),
new Score("江梦南",91),
new Score("微微",92),
new Score("夏至",93)
);
req.getSession().setAttribute("scores",scores);
resp.sendRedirect("score.jsp");
}
}
score.jsp文件
<%@ page import="java.util.Date" %>
<%@ 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"%>
成绩信息展示
成绩高于80
成绩高于80吗? ${requestScope.result}
成绩优秀
成绩良好
成绩中等
成绩很差
<% request.setAttribute("now", new Date()); %>
当前日期:
当前时间:
姓名
成绩
${score.name}
${score.score}
<% request.setAttribute("money", 12345.678); %>
货币:
数字:
百分比:
运行结果(点击链接"显示成绩"后):
对比版本:不使用EL表达式和JSTL标签
<%@ page import="com.servlet.Score" %>
<%@ page import="java.util.List" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<%@ page import="java.text.NumberFormat" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
成绩信息展示 (使用Scriptlet)
<%
Score zhangsan = (Score) session.getAttribute("zhangsan");
boolean result = false;
if (zhangsan != null && zhangsan.getScore() > 80) {
result = true;
out.println("成绩高于80");
}
request.setAttribute("result", result);
%>
成绩高于80吗? <%= request.getAttribute("result") %>
<%
if (zhangsan != null) {
if (zhangsan.getScore() > 90) {
%>
成绩优秀
<%
} else if (zhangsan.getScore() > 80) {
%>
成绩良好
<%
} else if (zhangsan.getScore() > 70) {
%>
成绩中等
<%
} else {
%>
成绩很差
<%
}
} else {
// 处理zhangsan为null的情况
out.println("未找到用户信息");
}
%>
<%
@SuppressWarnings("unchecked")
List scores = (List) session.getAttribute("scores");
if (scores != null && !scores.isEmpty()) {
for (Score score : scores) {
%>
<%
}
}
%>
姓名
成绩
<%= score.getName() %>
<%= score.getScore() %>
<%
Date now = new Date();
request.setAttribute("now", now);
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdfDateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
%>
当前日期:<%= sdfDate.format(now) %>
当前时间:<%= sdfDateTime.format(now) %>
<%
double money = 12345.678;
request.setAttribute("money", money);
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat numberFormatter = NumberFormat.getNumberInstance();
numberFormatter.setMaximumFractionDigits(2);
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
%>
货币:<%= currencyFormatter.format(money) %>
数字:<%= numberFormatter.format(money) %>
百分比:<%= percentFormatter.format(0.75) %>
区别分析
- EL表达式:
- 简洁:
${sessionScope.zhangsan.score > 80}一行代码替代了Scriptlet中获取对象、类型转换、null检查和条件判断的多行代码。 - 直观:语法接近自然语言,
zhangsan.score直接对应zhangsan.getScore(),可读性极高。
- 简洁:
- JSTL标签:
- 结构化:
<c:if>,<c:choose>,<c:forEach>等标签提供了清晰的逻辑结构,避免了Scriptlet中<% ... %>代码块与HTML标签的混乱交织。 - 专注视图:标签将Java逻辑封装起来,JSP页面更像一个专注于展示的模板。
- 结构化:
维护成本对比
- 使用 EL + JSTL:
- 低维护成本:
- 代码逻辑清晰,易于理解和追踪。
- 如需修改判断条件(如将
> 80改为>= 85),只需修改EL表达式中的数字,非常直观。 - 如需修改循环或格式化规则,只需调整标签的属性。
- 低维护成本:
- 不使用 EL + JSTL:
- 高维护成本:
- 代码逻辑分散在多个
<% ... %>块中,阅读时需要在Java代码和HTML之间频繁切换上下文。 - 修改逻辑时,需要在Java代码块中找到对应的
if或for语句,容易出错。 - 代码冗长,增加了阅读和理解的难度。
- 代码逻辑分散在多个
- 高维护成本:
健壮性与安全性对比
- 使用 EL + JSTL:
- 更健壮:
- EL表达式对
null有天然的免疫力。如果zhangsan为null,${zhangsan.score}会返回null,在条件判断中会被当作false,不会抛出NullPointerException。 - JSTL标签(如
<c:forEach>)在集合为null时通常会静默处理(不执行循环),增加了页面的稳定性。
- EL表达式对
- 更健壮:
- 不使用 EL + JSTL:
- 较脆弱:
- 必须手动进行
null检查(if (zhangsan != null)),否则程序可能因NullPointerException而崩溃。 - 类型转换错误(如将一个
String强制转为Score)也会导致运行时异常。
- 必须手动进行
- 较脆弱:
开发效率对比
- 使用 EL + JSTL:
- 高开发效率:
- 减少了大量重复的模板代码(如
getAttribute、类型转换、for循环结构)。 - 开发者可以更专注于业务逻辑和页面呈现,而非底层的Java语法细节。
- 减少了大量重复的模板代码(如
- 高开发效率:
- 不使用 EL + JSTL:
- 低开发效率:
- 需要编写大量模版代码,增加了编码工作量。
- 代码量大,也增加了出现语法错误的可能性。
- 低开发效率:
对比总结
| 对比维度 | 使用 EL表达式 + JSTL标签 | 不使用 (Scriptlet) |
|---|---|---|
| 代码简洁度 | 高,代码量少,逻辑清晰。 | 低,代码冗长,充满<% ... %>块。 |
| 可读性 | 高,语法直观,接近自然语言,结构分明。 | 低,Java代码与HTML标签混杂,难以阅读。 |
| 维护成本 | 低,逻辑集中,修改方便,不易出错。 | 高,逻辑分散,修改时需在Java与HTML间切换。 |
| 健壮性 | 高,自动处理null,减少空指针异常。 | 低,需手动处理null和类型转换,易出错。 |
| 开发效率 | 高,减少重复编码,专注业务。 | 低,编写大量样板代码,效率低下。 |
如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!




浙公网安备 33010602011771号