核心概念:请求与响应
在理解页面跳转之前,必须明白 Java Web 的基本工作模型:客户端(浏览器)发送请求 -> 服务器(Tomcat等)接收并处理 -> 服务器返回响应 -> 客户端接收并渲染响应。

页面跳转的本质,就是服务器在处理完请求后,通过响应来告诉浏览器下一步该做什么。
服务器端跳转 - forward
这种方式是服务器内部的行为,浏览器发送请求到服务器,服务器将请求转发给内部的另一个资源(如 Servlet 或 JSP)进行处理,并将最终的响应返回给浏览器,在这个过程中,浏览器的地址栏不会发生变化。
实现方式
主要在 Servlet 中使用 RequestDispatcher 对象来实现。
Servlet 代码示例

假设我们有两个 Servlet:ForwardServlet 和 TargetServlet。
ForwardServlet.java
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("/forward")
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ForwardServlet: 收到请求,准备转发...");
// 1. 可以在转发前向 request 中添加一些数据
req.setAttribute("messageFromForward", "你好,这是来自 ForwardServlet 的消息!");
// 2. 获取 RequestDispatcher 对象
// 参数是目标资源的相对路径,以斜杠 "/" 开头表示相对于当前 Web 应用的根目录
// 如果你的 TargetServlet 映射为 "/target",那么路径就是 "/target"
// 如果你的 target.jsp 放在 WEB-INF/views 目录下,路径就是 "/WEB-INF/views/target.jsp"
RequestDispatcher dispatcher = req.getRequestDispatcher("/target");
// 3. 执行 forward 跳转
// 注意:调用 forward() 方法后,当前 Servlet 的代码会立即停止执行,控制权完全交给目标资源。
// dispatcher.forward(req, resp);
}
}
TargetServlet.java
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("/target")
public class TargetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("TargetServlet: 收到转发的请求,开始处理...");
// 从 request 中获取 ForwardServlet 设置的数据
String message = (String) req.getAttribute("messageFromForward");
// 向客户端响应
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("<h1>这是目标页面</h1>");
resp.getWriter().println("<p>接收到的消息: " + message + "</p>");
resp.getWriter().println("<p>浏览器地址栏仍然是: /forward</p>");
}
}
工作流程
- 浏览器访问
http://localhost:8080/your-app/forward。 ForwardServlet被调用。ForwardServlet将数据存入request作用域,并调用forward方法将请求转发给TargetServlet。- 浏览器地址栏保持不变,仍然是
/forward。 TargetServlet接收请求,读取request中的数据,生成响应并返回给浏览器。- 浏览器显示
TargetServlet生成的 HTML 内容。
优点
- 共享数据:可以在多个 Servlet 或 JSP 之间共享
request作用域的数据,非常适合 MVC 模式中 Controller 和 View 之间的数据传递。 - 隐藏内部路径:可以将 JSP 等视图资源放在
WEB-INF目录下,防止用户直接通过 URL 访问,提高安全性。
缺点
- URL 不变:用户看到的 URL 是第一个请求的 URL,如果刷新页面,可能会重复执行第一个请求,导致数据重复提交(在登录后刷新,可能会重复注册)。
- 不能脱离当前 Web 应用:
forward只能跳转到当前应用内部的资源。
客户端跳转 - redirect
这种方式是服务器告诉浏览器:“你访问的资源不在这里,请去新的 URL 重新访问一次”,浏览器会收到这个新的 URL,并自动向该 URL 发起一次全新的请求,在这个过程中,浏览器的地址栏会更新为新的 URL。

实现方式
主要在 Servlet 中使用 HttpServletResponse 的 sendRedirect() 方法。
Servlet 代码示例
RedirectServlet.java
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("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("RedirectServlet: 收到请求,准备重定向...");
// 1. 同样可以设置数据,但注意:request 中的数据在重定向后无法传递到新页面!
// 因为这是两次独立的请求。
req.setAttribute("messageFromRedirect", "这条消息会丢失!");
// 2. 执行 redirect 跳转
// 参数是目标资源的完整 URL(可以是绝对路径,也可以是相对路径)
resp.sendRedirect("target2");
}
}
TargetServlet2.java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import HttpServletResponse;
import java.io.IOException;
@WebServlet("/target2")
public class TargetServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("TargetServlet2: 收到重定向过来的新请求,开始处理...");
// 尝试获取 RedirectServlet 设置的数据,会发现是 null
String message = (String) req.getAttribute("messageFromRedirect");
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("<h1>这是重定向的目标页面</h1>");
resp.getWriter().println("<p>无法获取到重定向前的消息: " + message + "</p>");
resp.getWriter().println("<p>浏览器地址栏已更新为: /target2</p>");
}
}
工作流程
- 浏览器访问
http://localhost:8080/your-app/redirect。 RedirectServlet被调用。RedirectServlet生成一个响应,状态码为302(Found) 或301(Moved Permanently),并在响应头Location中指定新的 URL(如/target2)。- 浏览器接收到这个响应,发现是重定向指令,于是立即中断当前请求,并自动向
/target2发起一个全新的 GET 请求。 - 浏览器地址栏变为
/target2。 TargetServlet2被调用,处理这个新请求,并生成响应返回给浏览器。
优点
- URL 更新:地址栏会显示最终的 URL,刷新页面不会导致重复提交操作,非常安全。
- 可以跳转到外部网站:
sendRedirect的参数可以是任何有效的 URL,如resp.sendRedirect("https://www.google.com");。
缺点
- 无法共享数据:两次请求是完全独立的,
request作用域的数据无法在它们之间传递。 - 性能开销:需要浏览器和服务器之间进行两次通信。
前端页面跳转 - JavaScript
这种方式完全在浏览器端执行,与服务器无关,通常用于用户操作后的即时跳转,例如点击按钮后跳转。
实现方式
在 HTML 或 JSP 页面中嵌入 JavaScript 代码。
示例 JSP (index.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>JavaScript 跳转示例</title>
<script>
// 1. 使用 window.location.href
function redirectByHref() {
// 可以是相对路径或绝对路径
window.location.href = "javascript-target.jsp";
}
// 2. 使用 window.location.assign
function redirectByAssign() {
window.location.assign("javascript-target.jsp");
}
// 3. 使用 window.location.replace
// replace() 和 href 的区别是:replace() 不会在浏览器历史记录中留下当前页面的记录,
// 用户点击后退按钮时,会直接跳到更早的页面,而不会回到这个页面。
function redirectByReplace() {
window.location.replace("javascript-target.jsp");
}
</script>
</head>
<body>
<h1>JavaScript 页面跳转</h1>
<button onclick="redirectByHref()">使用 href 跳转</button>
<button onclick="redirectByAssign()">使用 assign 跳转</button>
<button onclick="redirectByReplace()">使用 replace 跳转</button>
</body>
</html>
目标页面 (javascript-target.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>JavaScript 跳转目标</title>
</head>
<body>
<h1>成功通过 JavaScript 跳转到了这里!</h1>
<p>浏览器地址栏会立即更新。</p>
</body>
</html>
优点
- 即时响应:无需等待服务器响应,用户体验好。
- 灵活:可以根据用户行为、表单验证结果等动态决定是否跳转以及跳转到哪里。
缺点
- 功能受限:无法直接访问后端作用域(如
request,session)的数据,除非通过 AJAX 等技术配合。
框架层面的跳转 (推荐)
在现代 Java Web 项目中,我们很少直接在 Servlet 中手动处理页面跳转,通常我们会使用 Spring MVC 或其他 MVC 框架,它们提供了更优雅、更强大的方式。
Spring MVC 示例
Spring MVC 通过控制器方法返回一个字符串(或 View 对象)来决定视图的渲染,这个过程本质上还是 forward,但对开发者更友好。
配置视图解析器 (spring-mvc.xml)
<!-- 配置视图解析器,将逻辑视图名映射到实际的 JSP 文件 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/> <!-- JSP 文件的前缀 -->
<property name="suffix" value=".jsp"/> <!-- JSP 文件的后缀 -->
</bean>
控制器代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class MyController {
// 使用 ModelAndView
@GetMapping("/mv")
public ModelAndView modelAndViewDemo() {
ModelAndView mav = new ModelAndView("targetView"); // 逻辑视图名
mav.addObject("message", "来自 ModelAndView 的数据");
return mav; // Spring 会自动 forward 到 /WEB-INF/views/targetView.jsp
}
// 直接返回逻辑视图名
@GetMapping("/viewName")
public String viewNameDemo() {
// 方法返回值 "targetView" 会被视图解析器解析为 /WEB-INF/views/targetView.jsp
// 这本质上是一个 forward
return "targetView";
}
// 重定向 (客户端跳转)
@GetMapping("/springRedirect")
public String redirectDemo() {
// �值 "redirect:" 前缀告诉 Spring 执行一个客户端重定向
return "redirect:/targetView"; // 浏览器会重新请求 /targetView
}
}
JSP 页面 (/WEB-INF/views/targetView.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>目标页面</title>
</head>
<body>
<h1>Spring MVC 目标页面</h1>
<p>接收到的数据: ${message}</p>
</body>
</html>
Spring MVC 的方式将数据传递和视图选择解耦,代码更清晰,是当前 Java Web 开发的最佳实践。
总结与选择
| 方式 | 类型 | 浏览器地址栏 | 数据共享 | 适用场景 |
|---|---|---|---|---|
forward |
服务器端跳转 | 不变 | 可以 (request作用域) | MVC架构中,Controller向View传递数据;隐藏内部资源。 |
redirect |
客户端跳转 | 改变 | 不可以 | 防止表单重复提交;登录成功后跳转;跳转到外部网站。 |
| JavaScript | 前端跳转 | 改变 | 不可以 (除非AJAX) | 用户交互后的即时跳转;前端逻辑控制。 |
| Spring MVC | 框架层面 | 可变 (取决于返回的是视图名还是redirect:) |
可以 (Model) | 现代Java Web开发首选,代码清晰,职责分明。 |
如何选择?
-
如果你在使用原生 Servlet:
- 需要在多个后端组件间传递数据,并且不希望用户看到真实的内部 URL ->
forward。 - 完成一个操作(如登录、注册)后,需要将用户引导到新页面,并且防止刷新导致重复操作 ->
redirect。
- 需要在多个后端组件间传递数据,并且不希望用户看到真实的内部 URL ->
-
如果你在使用 Spring MVC 或类似框架:
- 默认使用返回视图名的方式(本质是
forward),这是最常见的情况,用于展示数据。 - 在需要防止重复提交或需要改变 URL 的场景下,使用
redirect:前缀(本质是redirect)。 - 在页面上有复杂的交互逻辑时,使用 JavaScript。
- 默认使用返回视图名的方式(本质是
-
永远不要在 Servlet 中使用
out.println()手动拼接 HTML 来做页面跳转,这是一种非常落后且难以维护的做法。
