哎,你好呀!我是 Agnes。很高兴你能把 JSP 这个“老熟人”重新拿出来聊聊。虽然现在的年轻人更爱 React、Vue 或者 Spring Boot 的纯后端开发,但 JSP 可是 Java Web 世界的“祖师爷”级别的存在。很多现在流行的模板引擎,其实都能看到它的影子。
今天我不跟你拽那些枯燥的教科书定义,咱们就像坐在咖啡馆里,我一边画图(用文字描述),一边给你拆解 JSP 到底是个什么玩意儿,它为什么能帮我们省掉那么多重复的 HTML 代码。准备好了吗?咱们开始“撸代码”!
一、 先别急着写代码,咱们聊聊“灵魂”:JSP 到底是什么?
想象一下,你是一家餐厅的老板(网站管理员)。客人(浏览器)来点菜。
- 纯 HTML 网站:就像一家菜单永远印在墙上的餐厅。如果今天特价菜变了,你得把整面墙砸了重刷。
- 动态网站:就像有个聪明的服务员(服务器),根据客人的需求,现场写单子。
JSP (JavaServer Pages) 就是那个“服务员”的笔记本。它的核心思想很简单:在静态的 HTML 页面里,嵌入 Java 代码片段。
当浏览器发出请求时,JSP 引擎(比如 Tomcat)会把这个 .jsp 文件“翻译”成一个 Servlet(Java 类),然后执行这个 Java 类,最后把生成的 HTML 发给浏览器。
关键点:对于浏览器来说,它收到的永远是普通的 HTML。它根本不知道背后是 JSP 在干活。这就是为什么我们说 JSP 是“服务器端”技术。
为什么还要学 JSP?
你可能会问:“Agnes,现在都 2026 年了,还用这老古董?” 嘿,别小看它!
- 理解根源:你不了解 JSP 的
request、session、application作用域,就很难真正理解 Spring 框架里的各种 Scope。 - 遗留系统:很多银行、政府、老企业的核心系统还在用 JSP,维护它们需要这种知识。
- 快速原型:对于简单的内部管理后台,JSP + Servlet 依然比搭建全套前后端分离架构要快得多。
二、 环境准备:别被安装劝退
为了让你立刻能看到效果,我用最经典的组合:Eclipse (或 IntelliJ IDEA) + Apache Tomcat + Java JDK。
步骤 1:下载 JDK
去 Oracle 或 OpenJDK 官网下个 JDK 17 或 21(LTS 版本)。装完后,记得配置 JAVA_HOME 环境变量。
步骤 2:下载 Tomcat
去 Apache Tomcat 官网下个 Binaries 版本的 Core(.zip 或 .tar.gz)。解压到你想放的文件夹,比如 D:\tomcat。
小技巧:启动 bin\startup.bat,浏览器访问 http://localhost:8080,看到那只猫就说明成功啦!
步骤 3:IDE 配置
以 IntelliJ IDEA 为例:
- 新建一个 Java Enterprise 项目,勾选 Web Application。
- 在 Project Structure (Ctrl+Alt+Shift+S) -> Server 里,添加你下载的 Tomcat 路径。
- 别忘了在 Artifacts 里把
WEB-INF/lib下的jsp-api.jar和servlet-api.jar加进去(通常新建 Web 项目会自动帮你加,这叫“Provided”依赖,部署时 Tomcat 自带,不用打进 war 包)。
三、 JSP 的“五脏六腑”:三大指令与九大内置对象
这是考试重点,也是实战核心。咱们分两部分讲:指令(Directives) 和 脚本元素(Scripting Elements)。
1. 三大指令
指令是给 JSP 引擎看的“全局设置”,它们不会直接产生输出,而是影响整个页面的处理方式。
(1) <%@ page ... %>:页面配置师
这是最常用的指令。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.List, java.util.ArrayList" %>
<%@ page isErrorPage="true" %>
<!--
contentType: 告诉浏览器这是什么类型,UTF-8 解决乱码神器!
import: 像 Java 一样,引入需要的类。
isErrorPage: 标记这个页面是否是错误处理页(通常配合 <error-page> 使用)。
-->
(2) <%@ include ... %>:文件拼接器
这是静态包含。在编译 JSP 之前,把另一个文件的内容原封不动地贴进来。
<!-- 比如把页头 nav 栏单独拿出来 -->
<%@ include file="header.jsp" %>
<div class="content">
这里是正文...
</div>
注意:如果
header.jsp改了,所有引用它的页面在下次编译时都会更新。但如果两个页面都有同名变量,可能会冲突!所以推荐用 动态包含(后面讲动作标签时会说)。
(3) <%@ taglib ... %>:标签库入口
JSP 支持自定义标签(Tag),比如 JSTL(JavaServer Pages Standard Tag Library)。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
这意味着你可以用 <c:if>、<c:forEach> 这样的标签来替代复杂的 Java 代码,让页面更干净。
2. 脚本元素:JSP 的灵魂代码
这部分是真正嵌入 Java 代码的地方。有三种写法,我强烈建议你只用第一种和第三种,少用第二种。
(1) 表达式 <%= %>:输出!
直接把一个变量的值打印到 HTML 中。注意,这里不用加分号。
<%-- 显示当前时间 --%>
<p>当前时间:<%= new java.util.Date().toLocaleString() %></p>
<%-- 显示后台传来的数据 --%>
<p>用户名:<%= request.getParameter("username") %></p>
(2) 脚本片段 <% %>:写逻辑
这里可以写任何合法的 Java 代码,包括变量定义、if-else、循环等。加分号!
<%
// 这里定义的变量,只在当前请求有效
int age = 25;
String name = "张三";
if (age > 18) {
out.println("您是成年人,可以进入。");
} else {
out.println("未成年人禁止入内。");
}
// 定义 List
List<String> fruits = new ArrayList<>();
fruits.add("苹果");
fruits.add("香蕉");
// 遍历输出
for (String fruit : fruits) {
out.println("<li>" + fruit + "</li>");
}
%>
(3) 声明 <%! %>:定义成员变量/方法
这里定义的内容会成为 JSP 转换后 Servlet 类的成员变量或成员方法。
<%!
// 成员变量:注意!这在全局共享,线程不安全!新手慎用的坑。
private int globalCount = 0;
// 成员方法
public String getGreeting(String name) {
return "Hello, " + name + "! Welcome to JSP world.";
}
%>
<!-- 使用刚才定义的方法 -->
<p><%= getGreeting("Agnes") %></p>
⚠️ 重要警告:在
<%! %>里定义的变量,因为是多线程共享的(每个请求一个线程,但 Servlet 实例只有一个),极易出现线程安全问题。所以,永远不要在 JSP 中用成员变量存业务数据,只用来存工具方法或常量。
3. 九个内置对象(Builtin Objects)
JSP 最爽的地方在于,你不用 new 任何对象,直接使用它们。这九个对象是 JSP 引擎自动创建好的。
| 对象名 | 类型 | 简介 | 常用场景 |
|---|---|---|---|
request |
HttpServletRequest |
客户端请求 | 获取参数 getParameter(),设置属性 setAttribute() |
response |
HttpServletResponse |
服务器响应 | 重定向 sendRedirect(),设置 Cookie |
out |
JspWriter |
输出流 | 相当于 System.out.println,但输出到浏览器 |
session |
HttpSession |
会话对象 | 存储用户登录信息、购物车等(跨请求保存) |
application |
ServletContext |
应用程序上下文 | 全局共享数据,比如网站计数器、配置信息 |
config |
ServletConfig |
配置对象 | 获取初始化参数 |
page |
Object (this) |
当前页面对象 | 极少直接用 |
pageContext |
PageContext |
页面上下文 | 其他八个对象的统一管理入口,高级用法 |
exception |
Throwable |
异常对象 | 仅在 isErrorPage="true" 的页面中可用 |
实战演示:怎么用 request 和 session?
假设我们做一个简单的登录跳转例子。
1. 登录表单 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>登录</title></head>
<body>
<h2>请登录</h2>
<!-- 如果登录失败,这里会显示错误信息 -->
<%
String errorMsg = (String) request.getAttribute("errorMsg");
if (errorMsg != null) {
out.println("<font color='red'>" + errorMsg + "</font>");
}
%>
<form action="doLogin.jsp" method="post">
用户名: <input type="text" name="username" /><br/>
密码: <input type="password" name="password" /><br/>
<input type="submit" value="登录" />
</form>
</body>
</html>
2. 处理登录 doLogin.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
request.setCharacterEncoding("UTF-8"); // 解决中文乱码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 简单的模拟验证(真实项目请查数据库)
if ("admin".equals(username) && "123456".equals(password)) {
// 登录成功:把用户信息存进 Session,这样跳转到其他页面也能拿到
session.setAttribute("user", username);
response.sendRedirect("welcome.jsp"); // 重定向到欢迎页
} else {
// 登录失败:把错误信息存回 Request,转发回登录页
request.setAttribute("errorMsg", "用户名或密码错误!");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
%>
3. 欢迎页 welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>欢迎</title></head>
<body>
<%
// 检查 session 里有没有用户
String user = (String) session.getAttribute("user");
if (user == null) {
// 没登录,直接跳回登录页
response.sendRedirect("login.jsp");
return; // 必须 return,否则下面代码还会执行
}
%>
<h1>你好, <%= user %>! 欢迎回来。</h1>
<a href="logout.jsp">退出登录</a>
</body>
</html>
4. 退出登录 logout.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// 销毁 session,让所有关联数据消失
session.invalidate();
response.sendRedirect("login.jsp");
%>
看,这就是 JSP 最经典的MVC 雏形:JSP 负责显示(View),Servlet(或 JSP 本身)负责逻辑(Controller)。
四、 进阶:JSP 动作标签(Action Tags)
除了 Java 代码,JSP 还提供了一些预定义的 XML 标签,叫“动作标签”。它们比脚本片段更安全、更整洁。
1. <jsp:include>:动态包含
区别于 <%@ include %>,这个是运行时包含。
- 静态包含:编译时合并文件,速度快,但灵活性差。
- 动态包含:运行时调用另一个页面,两个页面独立编译,可以传递参数。
<!-- 动态包含 page2.jsp,并传递参数 -->
<jsp:include page="page2.jsp">
<jsp:param name="color" value="blue"/>
</jsp:include>
在 page2.jsp 里可以用 request.getParameter("color") 拿到 “blue”。
2. <jsp:forward>:页面转发
相当于 request.getRequestDispatcher(...).forward(...)。
<!-- 直接跳转到 user.jsp,浏览器地址栏不变 -->
<jsp:forward page="user.jsp" />
3. <jsp:useBean>、<jsp:setProperty>、<jsp:getProperty>
这是 JSP 1.1 时代用来操作 Java Bean 的经典标签。虽然现在大家更爱用 EL 表达式(${}),但理解它们有助于读懂老代码。
<!-- 创建一个或获取名为 user 的 UserBean 对象,作用域为 page -->
<jsp:useBean id="user" class="com.example.User" scope="page"/>
<!-- 设置属性:将请求参数 "name" 的值赋给 user 的 name 属性 -->
<jsp:setProperty name="user" property="name" param="name"/>
<!-- 输出属性:相当于 user.getName() -->
<jsp:getProperty name="user" property="name"/>
五、 告别晦涩:EL 表达式与 JSTL
写 <%=(String)session.getAttribute("username")%> 太痛苦了,对吧?这时候,EL 表达式 (Expression Language) 和 JSTL (JSP Standard Tag Library) 登场了。它们是 JSP 现代化的关键。
1. EL 表达式:简洁的取值器
语法:${expression}
<!-- 以前这么写 -->
<%= request.getParameter("username") %>
<!-- 现在这么写 -->
${param.username}
<!-- 以前 -->
<%= ((User)session.getAttribute("user")).getName() %>
<!-- 现在 -->
${sessionScope.user.name}
EL 会自动查找四个作用域(pageScope, requestScope, sessionScope, applicationScope),找不到就不显示,也不会报错。
2. JSTL:替代 Java 脚本片段
要使用 JSTL,首先要在 WEB-INF/lib 放入 jstl.jar 和 standard.jar,并在 JSP 顶部加 taglib 指令。
条件判断 <c:if>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${sessionScope.user != null}">
<p>欢迎回来,${sessionScope.user.name}!</p>
</c:if>
<c:if test="${empty param.username}">
<p>请先登录。</p>
</c:if>
循环遍历 <c:forEach>
假设后台传了一个用户列表:
<%-- 后台代码: request.setAttribute("users", userList); --%>
<table border="1">
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
</tr>
<c:forEach items="${users}" var="user" varStatus="status">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.age}</td>
</tr>
</c:forEach>
</table>
varStatus="status" 可以让你在循环中拿到 index、count 等状态信息。
六、 实战大作业:一个简易的“留言板”系统
光说不练假把式。咱们来做一个完整的案例:简易留言板。
这个系统包含:
- 留言列表页
index.jsp - 发布留言处理页
post.jsp - 留言板数据存储在
Session中(简单演示,真实项目请用数据库)
项目结构
MyWebApp/
├── WEB-INF/
│ └── web.xml
├── index.jsp
├── post.jsp
└── success.jsp (可选,用于显示成功提示)
代码实现
1. index.jsp - 主页面
”`jsp <%@ page contentType=“text/html;charset=UTF-8” language=“java” %> <%@ taglib prefix=“c” uri=”http://java.sun.com/jsp/jstl/core” %>
<title>简易留言板</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.message { border-bottom: 1px solid #ccc; padding: 10px 0; }
.author { font-weight: bold; color: #2c3e50; }
.content { margin: 5px 0 5px 20px; }
.time { font-size: 0.8em; color: #999; }
form { background: #f9f9f9; padding:
