JSP 的本质是什么?和 Servlet 是什么关系?为什么现在很少用 JSP 了?
简化版
JSP(JavaServer Pages)的本质是「一种特殊的 Servlet」——它让你在 HTML 里嵌 Java 代码(写页面更方便),但第一次访问时,容器(Tomcat)会把 .jsp 文件翻译成一个 .java 的 Servlet 类、再编译成 .class 运行。所以 JSP 最终就是 Servlet,只是「写法」不同:Servlet 是「在 Java 里输出 HTML」(out.println("<html>"),写页面痛苦),JSP 是「在 HTML 里嵌 Java」(写页面方便),两者互补。JSP 有九大内置对象(request/response/session/application/out 等,不用声明直接用)。现在很少用 JSP,是因为前后端分离成了主流——后端只提供 JSON API,前端用 Vue/React 独立开发,页面渲染交给前端,JSP 这种「后端拼 HTML」的模式被淘汰了。
详细版
JSP 的本质:会被翻译成 Servlet:
你写的 hello.jsp(HTML 里嵌 Java):
<html><body>
<% for(int i=0; i<3; i++) { %>
<p>Hello <%= i %></p>
<% } %>
</body></html>
第一次访问时,Tomcat 的处理:
1. 翻译(translation):hello.jsp → hello_jsp.java(一个 Servlet 类)
JSP 里的 HTML 变成 out.write("..."),Java 代码原样保留
2. 编译(compilation):hello_jsp.java → hello_jsp.class
3. 执行:像普通 Servlet 一样,实例化、调用 _jspService() 方法处理请求
→ 之后再访问就直接用编译好的 class(除非 jsp 改了才重新翻译编译)
JSP 和 Servlet 的关系(互补):
| 维度 | Servlet | JSP |
|---|---|---|
| 本质 | Java 类 | 会被翻译成 Servlet |
| 擅长 | 处理逻辑(Java 为主) | 展示页面(HTML 为主,嵌 Java) |
| 写页面 | 痛苦(out.println 拼 HTML) | 方便(直接写 HTML) |
| 写逻辑 | 方便 | 混乱(逻辑和 HTML 混一起) |
| 定位 | 控制层(Controller) | 视图层(View) |
JSP 九大内置对象(不用声明直接用):
request(HttpServletRequest)、response(HttpServletResponse)
session(HttpSession)、application(ServletContext)
out(输出流)、pageContext(页面上下文)
config(ServletConfig)、page(当前页面对象 this)、exception(异常,错误页才有)
⚠️ JSP 的经典用法是配合 Servlet 做 MVC:Servlet 做控制器(处理请求、准备数据)→ 转发(forward)到 JSP → JSP 做视图(用嵌入的 Java 展示数据)。这是「Servlet + JSP 的 MVC 模式」(Model 2)。但现在这种「后端渲染页面」的模式被前后端分离取代——所以 JSP 是一个「你应该理解其原理、但新项目基本不用」的技术。
完整版教学
一、JSP 要解决什么:Servlet 写页面的痛苦
要理解 JSP,先看它诞生的背景——用纯 Servlet 写页面很痛苦:
// 用 Servlet 输出一个 HTML 页面(早期做法):
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<h1>用户列表</h1>");
for (User u : users) {
out.println("<p>" + u.getName() + "</p>"); // 在 Java 里拼 HTML,噩梦
}
out.println("</body>");
out.println("</html>");
}
问题很明显:在 Java 代码里用 out.println 一行行拼 HTML,既繁琐又易错——HTML 结构被淹没在字符串拼接里,改个页面布局要改一堆 Java 代码,前端和后端完全混在一起。JSP 的思路是「反过来」——不在 Java 里拼 HTML,而是在 HTML 里嵌 Java:主体是 HTML(写页面方便),需要动态内容的地方用 <% %> 嵌一小段 Java。这样写页面回归了 HTML 的自然写法。理解「JSP 是为了解决 Servlet 写页面的痛苦」,就理解了它和 Servlet 的互补关系。
二、JSP 的本质:翻译成 Servlet
JSP 最核心的认知是——它本质就是 Servlet,只是「写法」不同。JSP 文件在运行前会经历「翻译 + 编译」变成 Servlet:
JSP 的生命周期(第一次访问时):
① 翻译(Translation):容器把 xxx.jsp 翻译成 xxx_jsp.java(一个 Servlet 源码)
- JSP 里的静态 HTML → 变成 out.write("...")
- JSP 里的 Java 代码(<% %>)→ 原样搬进去
- JSP 表达式(<%= %>)→ 变成 out.print(...)
② 编译(Compilation):xxx_jsp.java → xxx_jsp.class
③ 加载执行:像普通 Servlet 一样实例化,调用 _jspService() 处理请求
之后再访问:直接用编译好的 class(除非 .jsp 文件被修改,才重新翻译编译)
关键理解:你写的 JSP,最终跑起来的是一个 Servlet——容器帮你把「HTML 里嵌 Java」的 JSP,翻译成了「Java 里拼 HTML」的 Servlet(out.write("<html>")),绕了一圈又回到 Servlet。所以「JSP 的本质是 Servlet」——它是「面向开发者友好的写法」+「运行时翻译成 Servlet」。这也解释了「JSP 为什么第一次访问慢」——第一次要翻译+编译,之后就快了(用编译好的 class)。理解「JSP = 会被翻译成 Servlet 的模板」,就抓住了它的本质。
三、JSP 和 Servlet 的分工:MVC
既然 Servlet 擅长逻辑、JSP 擅长页面,经典用法就是让它们分工协作做 MVC:
JSP + Servlet 的 MVC(Model 2)模式:
Servlet(Controller 控制器):
- 接收请求、处理业务逻辑、调用 Model(业务/数据层)
- 把结果数据放进作用域(如 request.setAttribute("users", list))
- forward 转发到 JSP
JSP(View 视图):
- 从作用域取数据(如 ${users} 或 <% %>)
- 用 HTML + 嵌入的展示逻辑渲染成页面
Model:JavaBean/实体(数据和业务)
流程:请求 → Servlet 处理 → 转发 → JSP 渲染 → 返回 HTML 页面
这种分工避免了两个极端:纯 Servlet 写页面痛苦、纯 JSP 写逻辑混乱。让 Servlet 专注控制和逻辑、JSP 专注展示,各展所长。这是早期 Java Web 的标准架构(SSH/SSM 时代的视图层)。为了让 JSP 里少写 Java(<% %> 混在 HTML 里也不优雅),后来还有 EL 表达式(${user.name})和 JSTL 标签库(<c:forEach>) 来替代 JSP 里的 Java 代码,让 JSP 更「纯粹」(接近纯标签,少嵌 Java)。理解「JSP 做 View、Servlet 做 Controller 的 MVC 分工」,就理解了 JSP 在传统架构里的位置。
四、九大内置对象:开箱即用
JSP 提供九大内置对象——在 JSP 里不用声明、直接就能用(因为翻译成 Servlet 时容器帮你准备好了):
九大内置对象(按重要性):
request → HttpServletRequest,当前请求(取参数、request 域)
response → HttpServletResponse,当前响应
session → HttpSession,会话(session 域)
application → ServletContext,应用(application 域,全局共享)
out → JspWriter,输出流(往页面写内容)
pageContext → 页面上下文(能访问所有作用域,最强)
config → ServletConfig,当前 JSP 的配置
page → 当前 JSP 页面对象(相当于 this)
exception → Throwable,异常对象(只有错误页 isErrorPage=true 才有)
这些对象「内置」的原因是——JSP 翻译成 Servlet 后,容器在 _jspService() 方法里自动声明并初始化了它们,所以你在 JSP 里直接用 request、session 就行,不用自己获取。其中 request/session/application 对应三大作用域(和前面 Servlet 的作用域一致),out 用来输出,pageContext 最强(能访问所有作用域)。理解「内置对象是容器翻译时帮你准备好的」,就明白了为什么 JSP 里能直接用它们——本质还是 Servlet 里的那些对象。
五、为什么现在很少用 JSP:前后端分离
JSP 曾是 Java Web 的主流视图技术,但现在新项目基本不用 JSP 了,核心原因是前后端分离成了主流:
JSP 时代(后端渲染):
后端(Servlet + JSP)负责一切:处理逻辑 + 拼装 HTML 页面
浏览器拿到的是后端渲染好的完整 HTML
→ 前后端代码耦合在一起(JSP 里既有 HTML 又有 Java)
前后端分离时代(现在):
后端:只提供 JSON API(@RestController 返回数据,不管页面)
前端:Vue/React 独立开发,拿 API 数据自己渲染页面
→ 前后端彻底解耦、各自独立开发部署
前后端分离淘汰 JSP 的原因:① 职责清晰(后端只管数据、前端只管展示,不再混在 JSP 里);② 前端体验更好(SPA、组件化、前端框架的能力远超 JSP);③ 多端复用(同一套 JSON API 能同时给 Web、App、小程序用,JSP 只能给 Web);④ 独立部署/并行开发(前后端分开,效率高)。所以「后端拼 HTML」的 JSP 模式,在「后端出 JSON、前端渲染」的分离架构下失去了位置。现在 Java 后端主流是 @RestController 返回 JSON + 前端框架,JSP 成了「了解原理即可、新项目不用」的遗留技术。理解「前后端分离淘汰了 JSP 的后端渲染模式」,就理解了它现状的根本原因。
六、JSP 的替代与现状
虽然 JSP 少用,但「服务端渲染视图」的需求没完全消失,有一些替代和场景:
JSP 的替代(如果还需要后端渲染模板):
Thymeleaf(Spring 官方推荐的模板引擎,比 JSP 现代、和 Spring 集成好)
Freemarker、Velocity(模板引擎)
→ 这些比 JSP 更纯粹(不嵌 Java 代码,纯模板语法)
仍需服务端渲染的场景:
- SEO 要求高的页面(搜索引擎爬虫更认服务端渲染的 HTML)
- 一些管理后台、简单页面(不值得上前端框架)
- 现代方案:Next.js/Nuxt 的 SSR(前端框架的服务端渲染,取代了 JSP 的位置)
现状总结:JSP 本身基本不用于新项目了(Spring Boot 甚至默认不推荐 JSP,推荐 Thymeleaf),但「服务端渲染」的思想仍在(换成了 Thymeleaf 或前端框架的 SSR)。面试问 JSP,重点是能讲清「JSP 本质是 Servlet(翻译编译)+ 和 Servlet 互补做 MVC + 前后端分离淘汰了它」——理解原理和它被淘汰的原因,比记住九大内置对象更重要。这也把 JSP 放进了「Java Web 从后端渲染到前后端分离」的演进脉络里。
记忆钩子:「JSP 本质是 Servlet——第一次访问时翻译(.jsp→.java Servlet)+ 编译(.java→.class)+ 执行;和 Servlet 互补:Servlet 做控制器(写逻辑方便)、JSP 做视图(写页面方便),配合做 MVC;九大内置对象(request/session/application/out 等,容器翻译时准备好);现在很少用因前后端分离(后端出 JSON、前端渲染),替代品 Thymeleaf」。
七、常见误区与追问
- 误区:JSP 和 Servlet 是两种不同的技术。 JSP 本质就是 Servlet——第一次访问时被容器翻译成 .java 的 Servlet 类再编译执行;只是「写法」不同(JSP 在 HTML 里嵌 Java、Servlet 在 Java 里拼 HTML)。
- 误区:JSP 比 Servlet 慢,因为它是解释执行的。 JSP 也是编译成 class 执行的(不是解释);只是第一次访问要翻译+编译所以慢,之后用编译好的 class 就和 Servlet 一样快。
- 误区:JSP 还是 Java Web 的主流。 现在新项目基本不用 JSP——前后端分离(后端出 JSON、前端框架渲染)成了主流,Spring Boot 也推荐 Thymeleaf 而非 JSP。
- 误区:JSP 的内置对象需要自己获取。 不用——JSP 翻译成 Servlet 时容器在 _jspService() 里自动声明并初始化了九大内置对象,你在 JSP 里直接用 request/session 即可。
- 追问:JSP 的执行过程是怎样的? 第一次访问:翻译(.jsp → .java Servlet 源码,HTML 变 out.write)→ 编译(.java → .class)→ 实例化调用 _jspService() 处理;之后访问直接用编译好的 class(除非 jsp 改了)。
- 追问:为什么前后端分离淘汰了 JSP? JSP 是「后端拼 HTML」,而分离架构是「后端出 JSON、前端渲染」——分离让职责清晰、前端体验更好、一套 API 多端复用、可独立部署,所以「后端渲染页面」的 JSP 模式被淘汰。
- 追问:JSP 的替代品是什么? 若仍需服务端渲染,用 Thymeleaf(Spring 推荐)、Freemarker 等模板引擎(比 JSP 纯粹不嵌 Java);现代前端框架的 SSR(Next.js/Nuxt)也取代了 JSP 的服务端渲染位置。
八、加强记忆
JSP(JavaServer Pages)的本质是「会被翻译成 Servlet 的模板」——它让你在 HTML 里嵌 Java(写页面方便),但第一次访问时容器(Tomcat)会「翻译」(.jsp → .java 的 Servlet 源码,HTML 变成 out.write)+「编译」(.java → .class)+ 执行,所以 JSP 跑起来就是个 Servlet(第一次慢因要翻译编译,之后快)。它和 Servlet 互补:Servlet 擅长逻辑(在 Java 里拼 HTML 写页面痛苦)、JSP 擅长页面(在 HTML 里嵌 Java 写页面方便),配合做 MVC(Model 2)——Servlet 做 Controller(处理请求、准备数据、forward 到 JSP)、JSP 做 View(取数据渲染页面),后来用 EL + JSTL 让 JSP 少嵌 Java。JSP 有九大内置对象(request/response/session/application/out/pageContext/config/page/exception,容器翻译时自动准备)。现在新项目基本不用 JSP,因为前后端分离成主流——后端只出 JSON API(@RestController)、前端用 Vue/React 独立渲染,职责清晰、多端复用、可独立部署,「后端拼 HTML」的 JSP 被淘汰,替代品是 Thymeleaf(Spring 推荐)等模板引擎或前端 SSR。答这题重点是「本质是 Servlet + 和 Servlet 互补做 MVC + 前后端分离淘汰它」。一句话「JSP 本质是翻译编译成的 Servlet、和 Servlet 互补做 MVC(JSP 视图 Servlet 控制器)、前后端分离后基本不用、替代品 Thymeleaf」。