JSP:在网页里混 Java(MVC 的 V)
Servlet 里手写 out.write("<h1>...") 拼 HTML,拼到第 50 行你会想死。JSP(JavaServer Pages)就是"反着来":先写好 HTML,需要动态插内容的地方嵌 Java。它本质上会被容器翻译成一个 Servlet——你以为在写网页,其实还是在写 Servlet。
show.jsp:HTML 为主,混少量动态代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://xmlns.jcp.org/jsp/jstl/core" %>
<html>
<body>
<h1>学生名单</h1>
<ul>
<%-- JSP 注释(用户看不到),下面 EL 表达式 ${} 直接取 request 里的 list --%>
<c:forEach items="${list}" var="s">
<li>${s.name} —— ${s.score} 分</li>
</c:forEach>
</ul>
</body>
</html>
论MVC:Servlet 和 JSP 怎么分工
M(Model 模型):数据本身,就是你的 JavaBean / record(学生对象)。V(View 视图):JSP,负责把数据画成网页。C(Controller 控制器):Servlet,接请求、调数据、挑页面。
一次请求的标准流程:浏览器 → Servlet(查数据库,把 list 塞进 request)→ forward 给 JSP → JSP 用 EL 把 list 显示出来。这套分工叫 MVC,是所有 Web 框架(包括 Spring MVC)的祖师爷。
为什么这么分:HTML 写在 Servlet 的 out.write("<h1>...") 里,改个样式要改 Java 代码;Java 业务写在 JSP 的 <% %> 里,前端想改页面要碰后端代码。MVC 把"长什么样"和"怎么算"分开,各改各的。
JSP 是怎么跑起来的(一次翻译,终身受益)
浏览器第一次访问 show.jsp
→ Tomcat 发现没跑过这个 JSP
→ 把 show.jsp 翻译成 show_jsp.java(一个 HttpServlet 子类)
→ 编译成 show_jsp.class,放进内存
→ 以后再访问,直接用编译好的 class,飞快
你改了 JSP,Tomcat 会自动检测并重新翻译——
但生产环境建议把 JSP 预编译好,别让用户第一次访问撞上编译延迟。
| JSP 零件 | 干什么用 |
|---|---|
指令<%@ ... %> | 页面级设置:contentType、导入 JSTL 标签库。 |
脚本片段<% ... %> | 直接嵌 Java 代码(能不用就不用,见第 10 章)。 |
EL 表达式${name} | 从 request/session/application 域里取值,自动判空,推荐用。 |
| JSTL c:forEach | 遍历集合,代替裸 for 循环。 |
| JSTL c:if | 页面上做条件判断。 |
EL 表达式常用隐式对象与 c:choose
${} 不是只会取 request 属性,它背后有几个自带的"隐式对象",查参数、查 Cookie 都靠它们。
| EL 写法 | 等价的 Java 代码 |
|---|---|
${name} | 依次从 page / request / session / application 找名字叫 name 的属性。 |
${param.xxx} | request.getParameter("xxx"),取请求参数。 |
${header.host} | 取请求头。 |
${cookie.token.value} | 名叫 token 的 Cookie 的值。 |
${sessionScope.loginUser} | 显式从 Session 域取,别靠默认搜索顺序。 |
多分支判断:c:choose / when / otherwise 相当于 if-else if-else
<c:choose>
<c:when test="${score >= 90}"><b>优秀</b></c:when>
<c:when test="${score >= 60}">及格</c:when>
<c:otherwise>不及格,补考见</c:otherwise>
</c:choose>
完整 MVC 案例:Servlet 控制器 + JSP 视图 + JavaBean 模型 + EL + JSTL
// —— M:Model,一个普通 JavaBean(学生)——
public class Student {
private String name;
private int score;
public Student(String name, int score) { this.name = name; this.score = score; }
public String getName() { return name; }
public int getScore() { return score; }
}
// —— C:Controller,Servlet 接请求、查数据、挑视图 ——
@WebServlet("/students")
public class StudentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1. 查数据(真实场景从 DAO 查数据库)
List<Student> list = List.of(
new Student("小明", 92),
new Student("小红", 88)
);
// 2. 塞进 request 域
req.setAttribute("studentList", list);
// 3. 转发给 JSP 展示(地址栏还是 /students)
req.getRequestDispatcher("/students.jsp").forward(req, resp);
}
}
// —— V:View,students.jsp 只负责显示 ——
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://xmlns.jcp.org/jsp/jstl/core" %>
<html><body>
<h1>学生名单</h1>
<table border="1">
<tr><th>姓名</th><th>分数</th></tr>
<c:forEach items="${studentList}" var="s">
<tr>
<td>${s.name}</td>
<td>
<c:choose>
<c:when test="${s.score >= 90}"><b style="color:green">${s.score}</b></c:when>
<c:when test="${s.score >= 60}">${s.score}</c:when>
<c:otherwise><span style="color:red">${s.score}</span></c:otherwise>
</c:choose>
</td>
</tr>
</c:forEach>
</table>
</body></html>
// 访问 http://localhost:8080/students,输出表格:
// 小明 92(绿色)
// 小红 88
看到 JSP 里 <% for(...) { ... } %> 塞了几十行业务逻辑——这叫"意大利面条式代码",页面又乱又难维护。规矩:业务逻辑全在 Servlet(或 Service)里,JSP 只负责"显示",最多写几个 c:forEach。现代前端(Vue/React)+ 后端返回 JSON 的架构,就是把这条规矩推到了极致。
1.(概念题)JSP 本质是什么?它怎么变成 Servlet 的?
查看答案
答案:JSP 是一个会被容器翻译成 Servlet 的文本模板。第一次访问时 Tomcat 把 .jsp 翻译成 _jsp.java 再编译成 class,以后访问直接用编译好的 class。
2.(概念题)MVC 三个字母分别对应什么?
查看答案
答案:Model 是数据(JavaBean/record),View 是 JSP(显示),Controller 是 Servlet(接请求、调数据、挑页面)。
3.(概念题)JSP 九大隐式对象是哪几个?
查看答案
答案:pageContext、request、response、session、application、out、config、page、exception。最常用的是 request(取参数)、session(取登录态)、out(写输出)。
4.(排错题)JSP 里 ${name} 输出空,怎么排查?
查看答案
答案:EL 表达式依次从 page/request/session/application 找属性。检查 Servlet 里 setAttribute 的名字和 JSP 里 ${} 里写的是否一致。名字拼错是最高频原因。