Java后端-JavaEE-Servlet深入理解与实战-3
背景
本文是《JavaEE 后端从小白到大神》修仙系列第三篇,正式进入JavaEE后端世界。若想详细学习请点击首篇博文,我们开始把。
第三篇:JSP 与 JSTL 表示层开发
- JSP 原理(编译为 Servlet);
- EL 表达式;
- JSTL 标签库;
- MVC 模式与 JSP 的角色;
- JSP + Servlet 结合构建 Web 应用;
- JSP 在现代架构中的替代方案(Thymeleaf、Vue、React 等);
一、 JSP 原理(编译为 Servlet)
JSP 本身不是可执行文件,必须经过 “翻译→编译→运行” 三个阶段,最终以 Servlet 形式处理请求。
1. 核心执行流程
|
|
-
翻译阶段:Tomcat 的
JspServlet将 JSP 页面翻译为 Java 源文件(Servlet),翻译规则如下:- HTML 静态内容 → 用
out.write()输出到响应流; <% Java 代码 %>→ 直接嵌入 Servlet 的_jspService()方法;<%= 表达式 %>→ 翻译为out.print(表达式);<%@ 指令 %>→ 转换为 Servlet 类的注解/配置(如pageEncoding对应response.setCharacterEncoding())。
- HTML 静态内容 → 用
-
编译阶段:Java 源文件被编译为
.class字节码(存储在 Tomcat 的work/Catalina/目录)。 -
运行阶段:容器加载 Servlet 实例,调用
_jspService(HttpServletRequest request, HttpServletResponse response)方法处理请求,动态生成 HTML 响应。
2. 翻译示例(JSP → Servlet)
JSP 页面(index.jsp):
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP 编译示例</title>
</head>
<body>
<h1>当前时间:<%= new Date() %></h1>
<% for (int i = 0; i < 3; i++) { %>
<p>循环输出:<%= i %></p>
<% } %>
</body>
</html>
翻译后的 Servlet 核心代码(简化版):
|
|
3. 关键特性
-
预编译:首次请求 JSP 时触发翻译+编译(有延迟),后续请求直接执行已编译的 Servlet;
-
自动重新编译:JSP 页面修改后,Tomcat 会检测到变化并重新翻译编译(开发环境友好);
-
内置对象:JSP 提供 9 个内置对象(如
request、response、session、out),无需手动创建,直接在页面使用。
| 内置对象 | 类型(类名) | 作用说明 |
|---|---|---|
| request | javax.servlet.http.HttpServletRequest |
封装客户端请求信息(如参数、头、路径等) |
| response | javax.servlet.http.HttpServletResponse |
响应客户端请求,控制输出、重定向等 |
| session | javax.servlet.http.HttpSession |
代表一次会话(浏览器与服务器之间的会话状态) |
| application | javax.servlet.ServletContext |
表示整个 Web 应用的上下文(所有用户共享) |
| out | javax.servlet.jsp.JspWriter |
向客户端输出内容(类似于 PrintWriter) |
| config | javax.servlet.ServletConfig |
表示当前 JSP 对应的 Servlet 配置信息 |
| pageContext | javax.servlet.jsp.PageContext |
JSP 页面级对象,封装其他 8 个对象的访问入口 |
| page | java.lang.Object |
代表当前 JSP 页面(Servlet 实例本身),等价于 this |
| exception | java.lang.Throwable |
表示当前页面中发生的异常,仅在错误页面(isErrorPage="true")中有效 |
(1)request
类型: HttpServletRequest
作用: 用于获取客户端发送的请求信息,包括参数、头信息、IP、请求方式等。
常用方法:
示例 JSP:
用户名:<%= request.getParameter("username") %>
(2)response
类型: HttpServletResponse
作用: 用于生成响应,比如重定向、设置响应头、输出数据类型等。
常用方法:
示例 JSP:
<%
response.setHeader("Refresh", "3;URL=home.jsp");
%>
页面3秒后自动跳转...
(3)session
类型: HttpSession
作用: 保存每个用户的会话数据(跨多个请求保持状态)。
常用方法:
示例 JSP:
欢迎你:<%= session.getAttribute("username") %>
(4)application
类型: ServletContext
作用: 整个 Web 应用的全局对象,所有用户共享。
常用方法:
示例 JSP:
访问总人数:<%= application.getAttribute("count") %>
(5)out
类型: JspWriter
作用: 输出内容到浏览器,是 JSP 的主要输出流。
常用方法:
示例 JSP:
<%
out.println("当前时间:" + new java.util.Date());
%>
注意:out 和 <%= ... %> 最终都会写入同一个缓冲区。
(6)config
类型: ServletConfig
作用: 代表 JSP 对应的 Servlet 配置信息(从 web.xml 传入的初始化参数)。
常用方法:
|
|
示例 JSP:
初始化编码:<%= config.getInitParameter("charset") %>
(7)pageContext
类型: PageContext
作用: JSP 页面范围对象,能访问所有其他内置对象,还能设置作用域变量。
常用方法:
示例 JSP:
<%
pageContext.setAttribute("greeting", "你好", PageContext.REQUEST_SCOPE);
%>
<jsp:forward page="next.jsp"/>
(8)page
类型: java.lang.Object(实际上是当前 Servlet 实例)
作用:
当前 JSP 页面自身,等价于 Java 中的 this。
示例 JSP:
<%= page.getClass().getName() %>
输出类似:
|
|
(9)exception
类型: java.lang.Throwable
作用:
保存 JSP 页面中发生的异常,仅在错误页面(isErrorPage="true")中可用。
示例 JSP(error.jsp):
<%@ page isErrorPage="true" %>
错误信息:<%= exception.getMessage() %>
二、 EL 表达式
EL(Expression Language)表达式用于 在 JSP 中简化 Java 代码访问数据,避免直接嵌入 <%= %> 代码,使页面更简洁。
1. 核心语法与作用
-
语法:
${ 表达式 }(如${user.name}、${sessionScope.user}); -
核心作用:
- 访问四大域对象的属性(
pageScope、requestScope、sessionScope、applicationScope); - 访问请求参数(
${param.username}等价于request.getParameter("username")); - 执行算术/逻辑运算(
${1+1}、${user.age > 18}); - 访问集合/数组(
${list[0]}、${map.key})。
- 访问四大域对象的属性(
2. 域对象访问规则
EL 表达式访问属性时,默认按域范围从小到大查找(pageScope → requestScope → sessionScope → applicationScope),找到即返回。
| 示例 | 等价 Java 代码 |
|---|---|
${username} |
pageContext.findAttribute("username") |
${requestScope.user} |
request.getAttribute("user") |
${sessionScope.cart} |
session.getAttribute("cart") |
${applicationScope.appName} |
application.getAttribute("appName") |
3. 常用内置对象
EL 提供 11 个内置对象,无需定义直接使用:
| 内置对象 | 作用 | 示例 |
|---|---|---|
param |
单个请求参数(String) |
${param.username} |
paramValues |
多值请求参数(String[]) |
${paramValues.hobby[0]} |
header |
单个请求头 | ${header.User-Agent} |
cookie |
访问 Cookie(通过 name.value 取值) |
${cookie.JSESSIONID.value} |
initParam |
全局初始化参数(web.xml) |
${initParam.db.url} |
4. 空值处理
EL 表达式对 null 友好,访问不存在的属性或 null 时返回空字符串(""),不会抛出空指针异常:
<!-- 若 user 为 null,输出空字符串,而非报错 -->
<div>${user?.name}</div> <!-- 可选链语法(EL 3.0+),避免空指针 -->
三、 JSTL 标签库
JSTL(JSP Standard Tag Library)是 JSP 标准标签库,提供一系列标签替代页面中的 Java 代码块(如循环、判断),使 JSP 页面更易维护。
1. 核心概念
-
依赖:需引入 JSTL 依赖(Maven):
-
标签库声明:使用前需在 JSP 页面声明标签库:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!-- 核心标签库 --> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <!-- 格式化标签库 -->
2. 核心标签库(<c: 前缀)
最常用的标签库,覆盖循环、判断、跳转等核心场景:
| 标签 | 作用 | 示例代码 |
|---|---|---|
<c:out> |
输出数据(防 XSS 攻击) | <c:out value="${user.name}" default="匿名用户"/> |
<c:if> |
条件判断 | <c:if test="${user.age > 18}">成年人</c:if> |
<c:choose>/<c:when> |
多条件判断 | <c:choose><c:when test="${score>=90}">优秀</c:when><c:otherwise>及格</c:otherwise></c:choose> |
<c:forEach> |
循环遍历(集合/数组/整数范围) | <c:forEach items="${list}" var="item" varStatus="status">${status.index}: ${item}</c:forEach> |
<c:set> |
定义变量/设置域属性 | <c:set var="total" value="${100+200}" scope="session"/> |
<c:redirect> |
重定向请求 | <c:redirect url="/login" /> |
<c:url> |
构建 URL(自动拼接 JSESSIONID) | <a href="<c:url value="/user?id=1"/>">用户详情</a> |
3. 格式化标签库(<fmt: 前缀)
用于日期、数字格式化,避免手动编写格式化代码:
<!-- 格式化日期(默认格式:yyyy-MM-dd) -->
<fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd HH:mm:ss"/>
<!-- 格式化数字(保留 2 位小数) -->
<fmt:formatNumber value="${order.amount}" pattern="0.00"/>
四、 MVC 模式与 JSP 的角色
MVC(Model-View-Controller)是分层架构模式,JSP+Servlet 组合天然契合 MVC 思想,各组件分工明确:
1. MVC 三层分工
| 层级 | 职责 | 技术实现 |
|---|---|---|
| Model(模型) | 封装业务数据和逻辑(如用户信息、数据库操作) | JavaBean 实体类、Service 层、DAO 层 |
| View(视图) | 展示数据,与用户交互 | JSP(结合 EL+JSTL)、HTML、CSS |
| Controller(控制器) | 接收请求、分发处理、返回视图/数据 | Servlet(或 Struts2、SpringMVC 框架) |
2. JSP 在 MVC 中的定位:纯 View 层
-
核心职责:仅负责数据展示,不包含复杂业务逻辑(如数据库查询、条件判断应放在 Servlet/Service 层);
-
最佳实践:JSP 中仅使用 EL 表达式获取数据、JSTL 标签控制页面结构,避免嵌入
<% Java 代码 %>(称为“无脚本 JSP”)。
3. 典型 MVC 流程(JSP+Servlet)
五、 JSP + Servlet 结合构建 Web 应用
以“用户列表查询”为例,完整实现 JSP+Servlet+EL+JSTL 的 MVC 应用:
1. 步骤 1:定义 Model(实体类+DAO 层)
|
|
2. 步骤 2:实现 Controller(Servlet)
|
|
3. 步骤 3:实现 View(JSP 页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>用户列表</title>
</head>
<body>
<h1>用户列表</h1>
<table border="1">
<tr>
<th>ID</th>
<th>用户名</th>
<th>年龄</th>
</tr>
<!-- 用 JSTL 循环遍历用户列表 -->
<c:forEach items="${userList}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.age}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
4. 步骤 4:配置与访问
-
无需额外配置(依赖
@WebServlet注解); -
访问 URL:
http://localhost:8080/项目名/user/list,即可看到用户列表页面。
六、 JSP 在现代架构中的替代方案(Thymeleaf、Vue、React 等)
随着前后端分离架构普及,JSP 因“前后端耦合、维护困难”逐渐被替代,主流方案分为两类:
1. 服务端模板引擎(替代传统 JSP+Servlet)
适用于“服务端渲染(SSR)”场景,保留服务端生成 HTML 的优势,但语法更简洁、功能更强:
| 技术 | 核心特点 | 适用场景 |
|---|---|---|
| Thymeleaf | 天然支持 HTML 静态原型(无标签时可正常显示)、语法优雅、Spring 官方推荐 | Spring Boot 项目、服务端渲染场景 |
| FreeMarker | 模板与数据分离彻底、性能高、支持复杂逻辑 | 电商网站、静态页面生成(如新闻详情) |
| Velocity | 语法简单、速度快(已停止维护) | legacy 项目 |
Thymeleaf 示例(替代上述 JSP):
|
|
2. 前后端分离架构(主流方案)
彻底分离前后端,后端提供 API 接口(JSON 数据),前端独立开发并调用接口,完全替代 JSP 的视图功能:
| 前端技术 | 核心特点 | 技术栈组合 |
|---|---|---|
| Vue.js | 轻量、易用、生态完善(Vue 3 + Vite) | 后端:Spring Boot(API)+ 前端:Vue 3 + Axios |
| React | 组件化强、适合复杂应用(如中台系统) | 后端:Spring Cloud + 前端:React + Ant Design |
| Angular | 全功能框架、适合企业级应用 | 后端:Node.js/Java + 前端:Angular |
前后端分离流程:
|
|
3. JSP 与现代方案的对比
| 特性 | JSP | 服务端模板引擎(Thymeleaf) | 前后端分离(Vue/React) |
|---|---|---|---|
| 前后端耦合度 | 高(Java+HTML混合) | 中(模板与逻辑分离) | 低(完全独立) |
| 开发效率 | 低(调试复杂) | 中(支持热部署) | 高(前后端并行开发) |
| 维护成本 | 高(代码混乱) | 中 | 低(职责清晰) |
| 适用场景 | 小型项目、快速原型 | 服务端渲染、SEO 需求高的项目 | 中大型项目、交互复杂的应用 |
七、总结
1. JSP 技术核心
-
本质:JSP 是 Servlet 的“模板化封装”,最终编译为 Servlet 执行;
-
生态:EL 表达式简化数据访问,JSTL 标签库替代 Java 代码块,三者结合构成 JSP 核心技术体系;
-
架构定位:在 MVC 模式中作为 View 层,负责数据展示,避免包含业务逻辑。
2. 技术演进结论
-
JSP 已逐步退出主流架构,仅适用于小型项目、legacy 系统维护;
-
现代项目优先选择:
- 服务端渲染需求 → Thymeleaf/FreeMarker;
- 交互复杂、中大型项目 → 前后端分离(Vue/React + 后端 API);
-
核心趋势:前后端解耦、开发效率提升、维护成本降低。
3. 关键启示
-
学习 JSP 核心价值:理解“服务端动态页面生成”的原理,为学习现代模板引擎和前后端分离架构奠定基础;
-
技术选型原则:根据项目规模、团队技术栈、SEO 需求选择合适的视图方案,而非固守传统技术。
文章作者 会写代码的小郎中
上次更新 2020-10-03
许可协议 CC BY-NC-ND 4.0