JSP如何实现请求转发 详解RequestDispatcher的使用方法与实际应用场景

在Java Web开发中,请求转发(Request Forwarding)是一种核心的服务器端跳转机制。与重定向(Redirect)不同,转发是在服务器内部完成的,客户端并不知道请求的中间处理过程。在JSP和Servlet技术栈中,
RequestDispatcher
是实现这一功能的关键接口。
本文将深入探讨JSP中如何使用
RequestDispatcher
实现请求转发,包括其基本语法、两种主要方法的区别、详细代码示例以及在实际项目中的应用场景。
1. 什么是请求转发与RequestDispatcher?
1.1 请求转发的概念
请求转发是指当Web容器收到一个客户端请求时,不直接响应,而是将请求在服务器内部传递给另一个资源(Servlet或JSP)进行处理。最终由最后一个资源生成响应并返回给客户端。
特点:
客户端无感知:
浏览器地址栏的URL不会改变。
共享请求范围:
转发过程中,请求对象(
HttpServletRequest
)和响应对象(
HttpServletResponse
)是同一个,因此可以共享Request域中的数据。
效率较高:
因为是服务器内部跳转,不需要再次进行HTTP网络请求。
1.2 RequestDispatcher 接口
javax.servlet.RequestDispatcher
是一个接口,定义了将请求转发到其他资源或包含其他资源响应的方法。Web容器(如Tomcat)负责为每个资源路径创建其实例。
2. 获取RequestDispatcher对象的三种方式
在使用转发之前,首先需要获取
RequestDispatcher
的实例。主要有以下三种方法:
通过ServletContext获取:
RequestDispatcher rd = getServletContext().getRequestDispatcher(“/path/to/resource.jsp”);
注意:路径必须以
/
开头,代表Web应用的根目录(Context Path)。
通过HttpServletRequest获取:
RequestDispatcher rd = request.getRequestDispatcher(“/path/to/resource.jsp”);
注意:路径同样以
/
开头,相对于当前Servlet的上下文根。
直接在JSP页面中使用PageContext(仅限JSP):
<% RequestDispatcher rd = pageContext.getRequestDispatcher("/path/to/resource.jsp"); %>
关键区别:
通常推荐使用
request.getRequestDispatcher()
,因为它在处理相对路径时更加灵活(虽然在Web应用中通常都使用绝对路径以避免混乱)。
3. RequestDispatcher的核心方法
RequestDispatcher
接口提供了两个核心方法来处理请求:
forward()
和
include()
。
3.1 forward() 方法
这是最常用的请求转发方法。
语法:
void forward(ServletRequest request, ServletResponse response)
作用:
将请求从当前Servlet转发到另一个资源。
限制:
在响应被提交(即调用
getWriter().flush()
或
getOutputStream().flush()
)
之前
调用。一旦响应被提交,再调用
forward()
会抛出
IllegalStateException
。
3.2 include() 方法
语法:
void include(ServletRequest request, ServletResponse response)
作用:
将另一个资源的响应内容包含到当前响应中。
区别:
forward()
是“替换”当前处理,
include()
是“插入”内容。通常用于页面布局(如页眉、页脚)。
4. 详细代码示例:实现登录后的页面跳转
为了详细说明,我们模拟一个简单的登录场景:用户提交表单,Servlet验证成功后,转发到欢迎页面。
4.1 场景设定
LoginServlet.java
: 处理登录逻辑。
login.jsp
: 登录表单。
welcome.jsp
: 登录成功后的欢迎页面,显示用户名。
4.2 代码实现
第一步:编写登录表单 (login.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


用户登录

用户登录

用户名:
密码:



第二步:编写处理逻辑的 Servlet (LoginServlet.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(“/LoginServlet”)
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

// 1. 设置编码
request.setCharacterEncoding(“UTF-8”);

// 2. 获取参数
String username = request.getParameter(“username”);
String password = request.getParameter(“password”);

// 3. 模拟业务逻辑验证 (实际项目中会查数据库)
if (“admin”.equals(username) && “123456”.equals(password)) {

// 4. 【关键步骤】将数据存入Request域
// 转发时,request对象是同一个,所以可以在转发前存入数据
request.setAttribute(“user”, username);

// 5. 【关键步骤】获取RequestDispatcher并执行转发
// 目标资源:/WEB-INF/pages/welcome.jsp (通常WEB-INF下的页面不能直接通过URL访问,只能通过转发访问,这是一种安全做法)
RequestDispatcher rd = request.getRequestDispatcher(“/WEB-INF/pages/welcome.jsp”);

// 6. 执行转发
rd.forward(request, response);

} else {
// 登录失败,转发回登录页并提示错误
request.setAttribute(“errorMsg”, “用户名或密码错误”);
RequestDispatcher rd = request.getRequestDispatcher(“login.jsp”);
rd.forward(request, response);
}
}
}
第三步:编写接收转发的页面 (welcome.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


欢迎页面



<% String user = (String) request.getAttribute("user"); // 如果直接通过地址栏访问此页面,user为null,需做非空判断 if (user == null) { out.println("请先登录“);
} else {
out.println(“

欢迎你, ” + user + “!

“);
out.println(“

您已成功通过RequestDispatcher转发至此页面。

“);
}
%>


4.3 示例解析
在这个例子中,
LoginServlet
并没有直接向浏览器写任何HTML内容,而是通过
rd.forward(request, response)
将控制权交给了
welcome.jsp
。浏览器地址栏依然显示
LoginServlet
,但看到的内容是
welcome.jsp
渲染的。这就是典型的
MVC模式
雏形:Servlet负责逻辑,JSP负责展示。
5. include() 方法的实际应用:页面布局
虽然
forward()
用于跳转,但
include()
用于“包含”。在实际开发中,我们经常使用它来构建动态的页面布局,避免每个页面都重复编写页眉和页脚。
5.1 示例代码
Header.jsp (页眉)

MyCompany Logo | 首页 | 关于我们

Footer.jsp (页脚)

Copyright © 2023 MyCompany Inc.

LayoutServlet.java (使用Include)
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;
import java.io.PrintWriter;

@WebServlet(“/showLayout”)
public class LayoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

response.setContentType(“text/html;charset=UTF-8”);
PrintWriter out = response.getWriter();

out.println(““);

// 1. 包含页眉
RequestDispatcher headerRD = request.getRequestDispatcher(“Header.jsp”);
headerRD.include(request, response);

out.println(“

“);
out.println(“

这是主要内容区域

“);
out.println(“

Servlet生成的动态内容在这里显示。

“);
out.println(“

“);

// 2. 包含页脚
RequestDispatcher footerRD = request.getRequestDispatcher(“Footer.jsp”);
footerRD.include(request, response);

out.println(““);
}
}
执行结果:
浏览器会看到一个完整的页面,其中页眉和页脚的内容被“插入”到了Servlet生成的响应流中。
6. 转发与重定向的区别(重要)
在面试或实际开发中,必须分清这两者:
特性
RequestDispatcher.forward()
HttpServletResponse.sendRedirect()
工作原理
服务器内部行为,服务器代为请求
服务器告诉浏览器:“你去访问那个地址”
浏览器感知
不知道
,地址栏URL不变
知道
,地址栏URL变为新地址
数据共享
共享同一个
request
对象
不共享
request
对象(产生两次请求)
效率
快(内部调用)
慢(需要两次HTTP往返)
目标资源
必须是同一个Web应用内的资源
可以是外部链接(如百度)或内部资源
刷新行为
刷新不会重复提交数据
刷新会重复提交数据(POST-Redirect-GET模式解决)
7. 实际应用场景总结
控制器模式 (Controller Pattern):
这是最经典的应用。所有的请求先由一个中央Servlet(控制器)接收,进行权限检查、日志记录、数据预处理,然后根据结果转发到具体的JSP页面。
保护敏感页面 (WEB-INF):
将JSP文件放在
WEB-INF
目录下。因为该目录下的文件无法通过URL直接访问,所以必须通过
RequestDispatcher
转发才能显示。这有效防止了用户未登录直接输入URL访问受保护页面的问题。
数据预处理与视图分离:
Servlet从数据库获取复杂的对象列表,封装好后放入Request域,然后转发给JSP。JSP只负责遍历显示,不包含任何Java查询逻辑。
错误处理:
当发生异常时,捕获异常后转发到统一的
error.jsp
页面显示友好错误信息。
8. 注意事项与常见错误
响应已提交异常:
如果在
forward
之前已经向输出流写入了数据并刷新(flush),再调用
forward
会抛出
java.lang.IllegalStateException
。
解决:
确保在转发前不要向
response.getWriter()
写任何内容。
路径问题:
在Servlet中转发时,路径建议始终以
/
开头,相对于Web应用的根路径(Context Path)。不要使用相对路径(如
../pages/home.jsp
),容易出错。
ServletChain(过滤器链):
forward
并不会停止过滤器的执行。如果在过滤器中调用了
forward
,后续的过滤器依然会执行,直到最后一个过滤器执行完才进入目标资源。
通过掌握
RequestDispatcher
,你就能在JSP和Servlet之间灵活地控制请求的流向,构建出结构清晰、安全的Java Web应用程序。