JSP 深度解析:从运行机制读懂 Java Web 的 “初代顶流“ 待定

JSP 深度解析:从运行机制读懂 Java Web 的 “初代顶流“ 待定

学习目标:

学习

在前后端分离成为主流的今天,JSP(Java Server Pages)似乎成了 "时代的眼泪"。但回溯 2000-2015 年的 Java Web 黄金期,它却是无数开发者入门动态网页的第一块敲门砖 —— 不用手动拼接 HTML 标签,直接在网页里写 Java 代码,就能轻松实现用户登录、数据查询等功能。

然而,这种 "便捷性" 的背后,藏着从底层原理衍生的安全漏洞。今天我们就从 JSP 的本质讲起,拆解它的运行流程、核心逻辑,以及那些至今仍威胁着遗留系统的风险点。

一、JSP 的本质:

很多人误以为 JSP 是独立的技术,其实它只是Servlet 的 "语法糖"。要理解 JSP,必须先搞懂 Servlet 的痛点 ——

Servlet 是纯 Java 类,要输出 HTML 页面,得用out.println()一行行拼接标签,比如:

java

// 纯Servlet代码,开发效率极低

response.setContentType("text/html");

PrintWriter out = response.getWriter();

out.println("");

out.println("");

out.println("欢迎," + username); // 拼接动态内容

out.println("");

out.println("");

而 JSP 允许开发者在 HTML 中直接嵌入 Java 代码,比如:

jsp

欢迎,<%= username %>

核心真相:JSP 文件不会被直接执行。服务器会先把它翻译成 Servlet 源文件(.java),再编译成字节码文件(.class),最终运行的还是 Servlet。我们写的 JSP 代码,只是让服务器帮我们自动完成了 "Java 代码→Servlet" 的转换工作。

二、JSP 完整运行流程:4 步从请求到响应

当你在浏览器输入http://xxx.com/user.jsp时,服务器(比如 Tomcat)会按以下 4 个步骤处理,每一步都藏着关键技术细节:

1. 请求拦截:服务器判断是否需要编译

服务器收到 JSP 请求后,会先去 "工作目录"(比如 Tomcat 的work/Catalina/localhost/项目名/)找对应的字节码文件(如user_jsp.class):

如果找到,且原 JSP 文件没有被修改过,直接跳过翻译和编译,执行字节码文件;

如果没找到(第一次请求),或原 JSP 文件被修改过,就进入 "翻译→编译" 阶段。

小知识:开发时修改 JSP 后不用重启服务器,就是因为服务器会检测文件修改时间,自动重新翻译编译。

2. 翻译阶段:JSP→Servlet 源文件(.java)

服务器的 JSP 引擎(比如 Tomcat 的 Jasper)会逐行解析 JSP 代码,把不同类型的内容转换成 Java 代码:

JSP 语法

作用

转换后的 Java 代码示例

<% 代码 %>

嵌入 Java 脚本(如循环、判断)

直接放在 Servlet 的_jspService()方法中

<%= 变量 %>

输出 Java 变量到 HTML

out.print(变量);

<%@ page ... %>

页面指令(如导入包、设置编码)

转换成类的注解或静态代码块

动态包含其他页面

request.getRequestDispatcher("xxx.jsp").include(request, response);

普通 HTML 标签

静态内容

用out.write("HTML标签");逐行输出

举个实际转换例子:原 JSP 代码:

jsp

<%@ page import="java.util.Date" %>

当前时间:<%= new Date() %>

<% if (new Date().getHours() < 12) { %>

早上好!

<% } %>

转换后的 Servlet 核心代码(简化版):

java

// 继承服务器提供的基础Servlet类

public class index_jsp extends org.apache.jasper.runtime.HttpJspBase {

// 处理请求的核心方法

public void _jspService(HttpServletRequest request, HttpServletResponse response) {

// 初始化输出流(JSP的out内置对象)

javax.servlet.jsp.JspWriter out = pageContext.getOut();

try {

response.setContentType("text/html");

out.write("\n");

out.write(" \n");

out.write(" 当前时间:");

out.print(new Date()); // 对应<%= new Date() %>

out.write("\n");

if (new Date().getHours() < 12) { // 对应<% if (...) %>

out.write("

早上好!

\n");

}

out.write(" \n");

out.write("");

} catch (Exception e) {

// 异常处理...

}

}

}

3. 编译阶段:Servlet 源文件→字节码(.class)

JSP 引擎调用 Java 编译器(javac),把生成的.java文件编译成.class字节码文件。这个字节码文件和普通 Java 类一样,会被服务器的类加载器加载到内存。

4. 执行阶段:生成响应返回浏览器

加载后的字节码文件会执行_jspService()方法(对应 Servlet 的service()方法),完成 3 件事:

通过request对象获取用户输入(如表单参数、URL 参数);

执行嵌入的 Java 代码(如查询数据库、处理业务逻辑);

通过out对象把动态内容(如时间、用户信息)嵌入 HTML,再通过response对象返回给浏览器。

三、JSP 核心逻辑:3 个简化开发的设计亮点

JSP 能成为 Java Web 的 "初代顶流",核心是它解决了 Servlet 的 3 个痛点,这些设计也是理解其漏洞的关键:

1. 内置对象:不用 new 就能用的 "工具集"

JSP 预定义了 9 个内置对象,开发者不用手动创建,直接在代码中使用,大幅减少重复代码。最常用的 5 个:

内置对象

类型

作用

request

HttpServletRequest

获取用户请求数据(如参数、Cookie)

response

HttpServletResponse

设置响应数据(如跳转、设置 Cookie)

session

HttpSession

存储用户会话信息(如登录状态)

out

JspWriter

向浏览器输出内容

pageContext

PageContext

获取其他内置对象,管理页面属性

比如要获取用户输入的 "用户名",直接写:

jsp

<% String username = request.getParameter("username"); %>

不用像 Servlet 那样手动声明HttpServletRequest对象。

2. 指令与动作元素:简化配置与页面复用

JSP 提供了专门的语法来处理配置和页面交互,不用写复杂的 Java 代码:

指令元素:以<%@开头,用于页面全局配置,比如<%@ page contentType="text/html;charset=UTF-8" %>(设置页面编码)、<%@ include file="header.jsp" %>(静态包含头部页面);

动作元素:以(页面跳转)、(传递参数)。

3. 生命周期自动管理:

JSP 对应的 Servlet 生命周期完全由服务器托管,开发者不用关心类的加载、实例化和销毁:

初始化阶段:第一次被访问时,服务器调用jspInit()方法(可重写,用于加载配置、初始化资源);

请求处理阶段:每次请求触发_jspService()方法(核心,处理业务逻辑);

销毁阶段:服务器关闭或应用卸载时,调用jspDestroy()方法(可重写,用于释放资源,如关闭数据库连接)。

学习时间:

学习时间为学习时间

学习时间

筋肉人

为学习时间

future

内容为笔记【有时比较抽象,有时比较过于详细,请宽恕。作者可能写的是仅个人笔记,筋肉人future】

学习产出:

技术笔记 1遍

有错误请指出,作者会及时改正

相关推荐

汽车安装电子狗多少钱
診怎么读
URL译为统一资源定位符-sci论文ris中的url也可以定位到论文网址,类似doi
PDF转换器