目录
- 什么是 EL 表达式?
- EL 表达式的基本语法
- EL 中的 11 个隐含对象
- EL 中的运算符
- EL 访问数据(重点)
- 访问 JavaBean 属性
- 访问 List/数组元素
- 访问 Map 键值对
- 访问作用域变量
- EL 的其他重要特性
- 空值处理
- 启用/禁用 EL
- 综合示例:一个完整的 JSP + EL + JavaBean 示例
什么是 EL 表达式?
EL (Expression Language),即表达式语言,是一种轻量级的脚本语言,旨在简化 JSP 页面中数据的访问。

在 JSP 2.0 之前,我们通常使用 JSP Scriptlet(<% ... %>)或 JSP Expression(<%= ... %>)来在页面上显示 Java 代码中的数据,这种方式存在以下问题:
- 代码混乱:HTML 和 Java 代码混杂在一起,可读性差,难以维护。
- 职责不清:JSP 页面本应负责显示(View),却包含了业务逻辑和数据处理,违反了 MVC 设计模式。
EL 表达式就是为了解决这些问题而生的,它提供了一种简洁、易于理解的方式来访问和显示数据,让 JSP 页面更加干净,专注于展示。
核心思想:让 JSP 回归到“视图”的角色,将复杂的逻辑处理交给 Servlet 或 JavaBean。
EL 表达式的基本语法
EL 表达式的语法非常简单,所有表达式都包含在 和 之间。

${expression}
要显示一个名为 name 的变量,只需写:
Hello, ${name}!
这比使用 JSP Expression <%= name %> 要简洁得多。
EL 中的 11 个隐含对象
EL 提供了一系列隐含对象,可以直接在 EL 表达式中使用,无需创建,它们就像是 JSP 内置对象的“简化版”或“只读版”。
| EL 隐含对象 | 对应 JSP 对象 | 描述 |
|---|---|---|
pageScope |
pageContext |
访问当前页面作用域 (PageContext) 的属性 |
requestScope |
request |
访问请求作用域 (HttpServletRequest) 的属性 |
sessionScope |
session |
访问会话作用域 (HttpSession) 的属性 |
applicationScope |
application |
访问应用作用域 (ServletContext) 的属性 |
param |
request.getParameter() |
获取请求参数的单个值(字符串) |
paramValues |
request.getParameterValues() |
获取请求参数的多个值(字符串数组) |
header |
request.getHeader() |
获取请求头的单个值(字符串) |
headerValues |
request.getHeaders() |
获取请求头的多个值(枚举) |
initParam |
application.getInitParameter() |
获取 web.xml 中配置的上下文初始化参数 |
cookie |
request.getCookies() |
获取客户端的 Cookie 对象 |
pageContext |
pageContext |
访问 PageContext 实例本身 |
注意:这些对象都是只读的,你不能在 EL 中给它们赋值,${param.name = "John"} 是错误的。
EL 中的运算符
EL 提供了一套完整的运算符,可以进行算术、逻辑、关系和空值判断。
| 运算符类型 | 运算符 | 描述 | 示例 |
|---|---|---|---|
| 算术 | , , , , div, , mod |
加、减、乘、除、取模 | ${price * 1.1} |
${10 div 3} (等同于 ) |
|||
| 关系 | , eq, , ne, <, lt, >, gt, <=, le, >=, ge |
等于、不等于、小于、大于等 | ${age > 18} |
${password ne '123456'} |
|||
| 逻辑 | &&, and, \|\|, or, , not |
与、或、非 | ${isVip and price > 100} |
| 空值判断 | empty |
判断一个对象是否为 null 或空(字符串长度为0、集合/数组大小为0) |
${empty userList} (userList 为 null 或空列表,返回 true) |
| 三元运算 | A ? B : C |
A 为 true,返回 B,否则返回 C | ${user.isVip ? 'VIP' : '普通用户'} |
EL 访问数据(重点)
EL 最强大的功能之一就是能够轻松地访问存放在不同作用域中的 Java 对象及其属性。
基础规则: 和 []
- (点号):用于访问对象的属性,要求属性名必须是合法的 Java 标识符(不能有空格、特殊字符)。
${user.name} // 访问 user 对象的 getName() 方法 [](方括号):用于访问对象的属性或集合/数组的元素,当属性名包含特殊字符(如user.name)、是变量或数字时,必须使用[]。${user['first-name']} // 属性名包含 '-',必须用 [] ${paramValues['hobbies'][0]} // 访问请求参数 hobbies 数组的第一个元素 ${list[0]} // 访问 list 的第一个元素
访问 JavaBean 属性
假设在作用域中有一个 User 对象,它有 name, age 属性。
// 假设 user 对象已存入 request 作用域
// request.setAttribute("user", new User("张三", 25));
// EL 会自动从 page -> request -> session -> application 作用域中查找
// 如果同名,scope 可以指定来源
${user.name} // 输出: 张三
${user.age} // 输出: 25
${user['age']} // 同样有效
访问 List/数组元素
假设 requestScope 中有一个 List<String>。
// List<String> skills = Arrays.asList("Java", "JSP", "EL");
// request.setAttribute("skills", skills);
${skills[0]} // 输出: Java
${skills[1]} // 输出: JSP
${skills[2]} // 输出: EL
访问 Map 键值对
假设 requestScope 中有一个 Map<String, Object>。
// Map<String, Object> data = new HashMap<>();
// data.put("username", "admin");
// data.put("loginTime", new Date());
// request.setAttribute("data", data);
${data.username} // 输出: admin (通过 key 访问 value)
${data['loginTime']} // 输出: 当前日期时间对象 (会调用 toString())
${data['loginTime'].year + 1900} // 访问 Date 对象的 year 属性 (注意:year 是从1900年开始的)
作用域查找机制
当你在 EL 中写 ${user.name} 时,EL 会按照以下顺序自动查找 user 这个变量:
pageScoperequestScopesessionScopeapplicationScope
一旦在某个作用域中找到,就立即返回,不再继续查找,如果所有作用域都找不到,EL 会返回空字符串 ,而不会抛出 NullPointerException。
如果你想明确指定从某个作用域获取,可以使用 scope 对象:
${requestScope.user.name} // 只从 request 作用域查找
${sessionScope.user.name} // 只从 session 作用域查找
EL 的其他重要特性
空值处理
这是 EL 的一个巨大优势,当访问一个不存在的属性或对象时,EL 会优雅地处理,而不会导致页面崩溃。
<%
User user = null;
request.setAttribute("user", user);
%>
// 使用 JSP Expression,会抛出 NullPointerException
// <%= ((User)request.getAttribute("user")).getName() %>
// 使用 EL,会安全地返回空字符串
${user.name} // 输出: ""
${empty user} // 输出: true
启用/禁用 EL
- 全局禁用:在
web.xml中配置,会禁用整个 Web 应用中的 EL。<jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <el-ignored>true</el-ignored> </jsp-property-group> </jsp-config> - 页面级禁用:在 JSP 页面中使用
isELIgnored指令。<%@ page isELIgnored="true" %> <%-- 从此行开始,所有 ${...} 都会被当作普通文本输出 --%>
综合示例:一个完整的 JSP + EL + JavaBean 示例
目标:创建一个 JSP 页面,显示一个用户信息和他的订单列表。
创建 JavaBean (User.java 和 Order.java)
// User.java
package com.example;
import java.util.List;
public class User {
private String name;
private int age;
private List<Order> orders;
// 构造器、Getter/Setter 方法
public User(String name, int age, List<Order> orders) {
this.name = name;
this.age = age;
this.orders = orders;
}
public String getName() { return name; }
public int getAge() { return age; }
public List<Order> getOrders() { return orders; }
}
// Order.java
package com.example;
public class Order {
private String orderId;
private double amount;
// 构造器、Getter/Setter 方法
public Order(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
}
public String getOrderId() { return orderId; }
public double getAmount() { return amount; }
}
创建 Servlet (UserServlet.java) 来准备数据
// UserServlet.java
package com.example;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 准备用户数据
List<Order> orders = Arrays.asList(
new Order("ORD001", 99.99),
new Order("ORD002", 199.50)
);
User user = new User("李四", 30, orders);
// 将 User 对象存入 request 作用域
request.setAttribute("user", user);
// 转发到 JSP 页面
request.getRequestDispatcher("/user_info.jsp").forward(request, response);
}
}
创建 JSP 页面 (user_info.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>用户信息</title>
</head>
<body>
<h1>用户详情</h1>
<hr>
<h2>基本信息</h2>
<p>姓名: ${user.name}</p>
<p>年龄: ${user.age}</p>
<p>是否成年: ${user.age > 18 ? '是' : '否'}</p>
<hr>
<h2>订单列表</h2>
<c:if test="${not empty user.orders}">
<table border="1" cellpadding="5">
<tr>
<th>订单ID</th>
<th>订单金额</th>
</tr>
<c:forEach items="${user.orders}" var="order">
<tr>
<td>${order.orderId}</td>
<td>¥${order.amount}</td>
</tr>
</c:forEach>
</table>
</c:if>
<c:if test="${empty user.orders}">
<p>该用户暂无订单。</p>
</c:if>
</body>
</html>
注意:上面的 JSP 用到了 JSTL (JSP Standard Tag Library) 的
<c:if>和<c:forEach>标签,在实际开发中,EL 和 JSTL 是黄金搭档,EL 负责提供数据,JSTL 负责逻辑控制和循环。
访问流程:
- 用户访问
http://localhost:8080/your-app/user。 UserServlet被调用,创建User对象并存入request作用域。Servlet将请求转发到user_info.jsp。user_info.jsp页面被解析,EL 表达式${user.name}从request作用域中找到user对象,并调用其getName()方法获取值 "李四",然后显示在页面上。- 同理,
${user.orders}获取订单列表,<c:forEach>标签遍历这个列表,并用 EL 显示每个订单的orderId和amount。
| 特性 | 描述 |
|---|---|
| 简洁性 | 语法 极其简洁,替代了繁琐的 <%=%> 和 <%...%>。 |
| 可读性 | 代码清晰,HTML 和数据访问逻辑分离,页面更易维护。 |
| 自动查找 | 自动在四个作用域中查找变量,简化了开发。 |
| 安全性 | 优雅地处理空值,避免 NullPointerException。 |
| 只读性 | 专注于数据展示,符合 MVC 思想,防止在视图中修改数据。 |
| 运算能力 | 内置丰富的运算符,可以在页面进行简单的逻辑判断和计算。 |
最佳实践: 在现代 JSP 开发中,应优先使用 EL 表达式来显示数据,并结合 JSTL 标签来处理流程控制、循环等逻辑,尽量避免在 JSP 页面中使用 Scriptlet,以保持代码的整洁和可维护性。
