Servlet 作为控制器,负责处理业务逻辑和请求,然后将处理结果(数据)传递给 JSP 视图层进行展示,传递参数主要有三种方式,它们各有特点,适用于不同的场景。

核心思想
Servlet 和 JSP 的本质区别在于:
- Servlet: Java 代码,负责逻辑控制。
- JSP: HTML + Java 代码片段,负责数据展示。
Servlet 向 JSP 传参的本质就是:Servlet 在处理完请求后,将数据存入某个“容器”中,然后通过 forward 或 redirect 方式将请求“转发”或“重定向”到 JSP 页面,JSP 再从这个“容器”中取出数据并展示。
重要提示:在 Servlet 中传递数据给 JSP,强烈推荐使用
RequestDispatcher.forward()进行请求转发,而不是response.sendRedirect()进行重定向。
- 请求转发 (
forward):服务器内部行为,浏览器地址栏不会改变,数据通过HttpServletRequest对象传递,就像一个方法调用,数据可以共享。- 重定向 (
sendRedirect):浏览器行为,服务器返回一个 302 状态码和新的 URL,浏览器会向这个新 URL 发起新的请求,数据无法通过HttpServletRequest共享,必须通过 URL 参数或 Session 来传递,这会导致 JSP 无法直接获取到 Servlet 存入 request 的属性。
三种主要传递方式
我们将通过一个完整的例子来演示这三种方式。
场景: 用户在 Servlet 中计算 1 + 1 的结果,然后将结果和一条消息传递给 JSP 页面进行显示。
准备工作
- 创建一个 Web 项目 (例如在 Eclipse 或 IntelliJ IDEA 中)。
- 配置
web.xml(如果使用较新的 Servlet API,可以省略,但为了兼容性,我们这里写一下)。
<!-- web.xml -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ParameterPassingServlet</servlet-name>
<servlet-class>com.example.ParameterPassingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ParameterPassingServlet</servlet-name>
<url-pattern>/passParams</url-pattern>
</servlet-mapping>
</web-app>
使用 request.setAttribute() 和 request.getRequestDispatcher().forward()
这是最常用、最推荐的方式,它通过将数据作为请求属性存储在 HttpServletRequest 对象中来实现传递。
Servlet 代码 (ParameterPassingServlet.java)
package com.example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/passParams") // 使用注解简化配置,可以不用在 web.xml 中配置
public class ParameterPassingServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 设置请求属性
// 参数1: 属性名 (String),在 JSP 中通过这个名来取值
// 参数2: 属性值 (Object),可以是任意 Java 对象
request.setAttribute("message", "Hello from Servlet!");
request.setAttribute("result", 2); // 1 + 1 的结果
// 2. 获取请求分发器,指定要转发的 JSP 页面
// 注意:这里的路径是相对于 Web 应用的根目录 (webapp) 的
request.getRequestDispatcher("/result.jsp").forward(request, response);
}
}
JSP 代码 (result.jsp)
在 JSP 中,我们使用 EL (Expression Language) 表达式 来轻松获取 request 中的属性,EL 是 JSP 2.0 及以后版本推荐的获取数据的方式,比传统的 JSP Scriptlet (<% ... %>) 更简洁、更安全。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>Result Page</title>
</head>
<body>
<h1>从 Servlet 传递过来的参数</h1>
<p>消息: ${message}</p>
<p>计算结果: ${result}</p>
<!-- 也可以使用 JSTL 的 c:out 标签,它更安全,可以防止 XSS 攻击 -->
<%-- <p>消息: <c:out value="${message}"/></p> --%>
</body>
</html>
访问流程:
- 浏览器访问
http://localhost:8080/your-app-name/passParams。 ParameterPassingServlet被调用。- Servlet 将
message和result存入request对象。 - Servlet 将请求转发到
/result.jsp。 result.jsp执行,EL 表达式${message}和${result}从request作用域中取出对应的值并显示。- 浏览器地址栏仍然是
/passParams。
使用 request.setAttribute() 和 response.sendRedirect()
这种方式不推荐用于传递数据,因为它会丢失 request 中的所有属性,但它偶尔会用于需要改变 URL 的场景(在 POST 请求后重定向到 GET 请求页面,避免表单重复提交)。
为了在重定向时传递数据,我们需要将数据拼接到 URL 中。
Servlet 代码
package com.example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/redirectParams")
public class RedirectServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 设置请求属性 (这部分其实没有用,因为重定向后 request 对象是新的)
request.setAttribute("message", "This message will be LOST!");
// 2. 将数据作为 URL 参数拼接
String msg = "Hello from Redirect!";
int res = 2;
// response.sendRedirect 的路径必须是绝对路径或相对于当前应用的路径
// 使用 URLEncoder.encode() 对参数进行编码,防止特殊字符导致 URL 错误
String encodedMsg = java.net.URLEncoder.encode(msg, "UTF-8");
response.sendRedirect("redirect_result.jsp?message=" + encodedMsg + "&result=" + res);
}
}
JSP 代码 (redirect_result.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>Redirect Result Page</title>
</head>
<body>
<h1>通过 URL 参数传递过来的数据</h1>
<p>消息: ${param.message}</p>
<p>计算结果: ${param.result}</p>
<!-- param 是 JSP 的隐式对象,专门用来获取请求参数 -->
</body>
</html>
访问流程:
- 浏览器访问
http://localhost:8080/your-app-name/redirectParams。 RedirectServlet被调用。- Servlet 发送一个 302 响应,告诉浏览器去访问
http://localhost:8080/your-app-name/redirect_result.jsp?message=Hello%20from%20Redirect!&result=2。 - 浏览器立即发起一个新的 GET 请求到新的 URL。
redirect_result.jsp被调用,EL 表达式${param.message}和${param.result}从新的request对象的参数列表中获取值。- 浏览器地址栏变为
.../redirect_result.jsp?message=...&result=...。
使用 HttpSession
当数据需要在多个请求之间共享时(用户登录信息),就应该使用 HttpSession,它是一个用户级别的会话对象,数据在整个会话期间都存在。
Servlet 代码
package com.example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/sessionParams")
public class SessionServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取或创建 Session 对象
HttpSession session = request.getSession();
// 2. 将数据存入 Session 作用域
session.setAttribute("user", "John Doe");
session.setAttribute("loginTime", new java.util.Date().toString());
// 3. 转发到 JSP 页面
request.getRequestDispatcher("/session_result.jsp").forward(request, response);
}
}
JSP 代码 (session_result.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>Session Result Page</title>
</head>
<body>
<h1>从 Session 中获取的数据</h1>
<p>用户名: ${sessionScope.user}</p>
<p>登录时间: ${sessionScope.loginTime}</p>
<!-- sessionScope 是 EL 的隐式对象,明确指定从 Session 作用域查找 -->
<!-- 也可以直接写 ${user},EL 会按 page -> request -> session -> application 的顺序查找 -->
</body>
</html>
访问流程:
- 浏览器访问
http://localhost:8080/your-app-name/sessionParams。 SessionServlet被调用,创建 Session 并存入数据。- 请求被转发到
session_result.jsp。 - JSP 从
session作用域中取出数据并显示。 - 即使用户之后访问其他页面,只要 Session 未失效,这些数据依然可以通过
${user}获取。
总结与对比
| 特性 | request.setAttribute() + forward() |
sendRedirect() + URL 参数 |
session.setAttribute() |
|---|---|---|---|
| 作用域 | Request 作用域 (仅当前请求有效) | Request 作用域 (新请求) | Session 作用域 (整个会话有效) |
| 数据可见性 | 数据不会暴露在 URL 中 | 数据暴露在 URL 中 | 数据不会暴露在 URL 中 |
| 安全性 | 高 | 低 (敏感信息不能放 URL) | 高 |
| 数据大小 | 无限制 (受服务器内存影响) | 受 URL 长度限制 (2KB) | 无限制 (受服务器内存影响) |
| 适用场景 | 最常用,控制器向视图传递数据,一次请求完成。 | 需要改变 URL,或跨应用传递简单数据。 | 用户状态管理,如登录信息、购物车等需要跨页面共享的数据。 |
| 代码示例 | request.setAttribute("key", value);request.getRequestDispatcher("xxx.jsp").forward(req, resp); |
response.sendRedirect("xxx.jsp?key=value"); |
request.getSession().setAttribute("key", value); |
| JSP 取值 | ${key} |
${param.key} |
${sessionScope.key} 或 ${key} |
最佳实践
- 优先使用
request.setAttribute()+forward():这是 MVC 模式中 Controller 向 View 传递数据的标准方式,简洁、安全、高效。 - 避免在 URL 中传递敏感信息:永远不要使用 URL 参数传递密码、Token 等敏感数据。
- 合理使用 Session:Session 会占用服务器内存,不要滥用,只在真正需要跨请求共享数据时使用,并且在数据不再需要时及时移除 (
session.removeAttribute()) 或使 Session 失效 (session.invalidate())。 - 使用 EL 和 JSTL:在 JSP 中,尽量使用 EL 表达式和 JSTL 标签库来处理数据,而不是使用过时的 Scriptlet (
<% ... %>),这样代码更清晰、更易于维护。
