Servlet规范定义了一个接口用于请求转发,即RequestDispatcher,该接口有Servlet引擎提供实现,它从客户端接受请求并把该请求转发到容器的任何资源上(包括Servlet,JSP,,HTML等)。虽然它可以包装任何资源,但是Servlet规范建议尽量只用来包装Servlet。RequestDispatcher接口规定了两个方法:
有一点必须强调:一个RequestDispatcher只能转发或者包含同一个Web应用中的资源。以Tomcat的容器模型为例(图是许令波画的,在此借用一下,希望不要介意):
真正管理Servlet的是Context容器,一个Context容器对应着一个Web应用。前面说了,RequestDispatcher是有Servlet容器创建的,那么这个RequestDispatcher对象只能在创建它的Context返回内实现转发。需要与这种情况区分开来:比如上图有两个Context,假设现在在左边的Context,然后获得了右边Context的ServletContext对象contextR,通过调用contextR.getRequestDispatcher()获得RequestDispatcher对象,重定向到右边的Context,貌似使用RequestDispatcher从左边的Context重定向到了右边的Cotnext上了。但是请注意,这个RequestDispatcher对象是右边的Context创建的,它能且只能转发到此Context,并不是左边的Context创建的RequestDispatcher重定向到了右边的Context上。
获得RequestDispatcher
RequestDispatcher是容器创建的,程序员无法创建,ServletContext和ServletRequest中都定义了获得RequestDispatcher的方法,先来看ServletContext接口中定义的两个用于获取的方法:
/**
* 返回包装了某个路径所指定的资源的RequestDispatcher对象
* 传递给该方法的路径必须以“/”开头,"/"代表当前Web应用的根
* 目录,WEB-INF中的内容对RequestDispatcher对象可见
*/
RequestDispatcher getRequestDispatcher(java.lang.String path);
/**
* 返回包装了某个Servlet或者JSP的RequestDispatcher对象
* 传递给该方法的参数是在Web应用中指定的Servlet或JSP的名称
* 可以通过调用ServletConfig.getSerlvetName()确定
*/
RequestDispatcher getNamedDispatcher(java.lang.String name)
注意,getNamedDispatcher()方法只能获得Serlvet或JSP的包装对象。ServletRequest接口中也提供了获取的方法:
/**
* 与ServletContext中的getRequestDispatcher(String)的区别:
* 可以使用"/","/"代表当前Web应用的根目录外,这个和前面相同
* 出了使用"/"开头的外,还可以使用不以"/"开头的的相对路径
*/
RequestDispatcher getRequestDispatcher(java.lang.String path);
有了RequestDispatcher对象,那就来看看它可以做写什么吧。
include实现资源包含
include方法用于将RequestDispatcher对象封装的资源内容作为当前相应的一部分包含进来。其实就是相当于组装,比如一个页面包含几个部分,每个部分放到不同的页面去实现,然后使用include方法将几个页面整合到到一个页面里,在调用include方法之前和之后的输出都是有效的。它们共享同一个request和response,不过被包含的页面不能改变响应消息的状态码和状态头,如果它存在这样的语句,这也设置将被忽略。下图展示了include的结构:
下面给出一个示例:
/**
* 包含文件和被包含文件是同一个文件,请求和处理都是相同的
* 只不过在被包含文件中设置响应消息的状态吗和响应头全部是小
*/
@WebServlet("/dispatch/Including")
public class Including extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String value = "我在冰封的深海找寻希望的缺口,却在午夜惊醒时,蓦然瞥见绝美的月光";
//给request添加属性,看看被包含的页面能否获得此属性
request.setAttribute("content", value);
out.println("-------------------------<br/>");
//获得被包装资源的RequestDispatcher对象
RequestDispatcher dispatch = getServletContext().getRequestDispatcher("/dispatch/Included");
//包含进来
dispatch.include(request, response);
out.println("<br/>----------end------------");
}
}
---------------------------------------------------------------
/**
* 被包含的页面
*/
@WebServlet("/dispatch/Included")
public class Included extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里的设置将被忽略
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
//获得request中的属性
out.println(request.getAttribute("content"));
/*这里的PrintWriter和包含页面中的PrintWriter是同一个对象
* 如果在这里把输出流关闭了,则包含页面中位于include方法的
* 内容将不会被输出了
*/
//out.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
在包含页面并没有设置编码方式,但是在被包含页面中设置了使用UTF-8编码,但是输出结果却是这样的:
-------------------------
????????????????????????????????
--------------end----------
这样说了在被包含页面中设置的响应头被忽略了。
forward实现请求转发
forward方法用于将一个请求转发到RequestDispatcher对象封装的资源,Servlet程序在调用这个方法进行转发之前可以对请求进行一些前期预处理,在调用forward方法时需要注意一下几点:
(1)在调用forward方法之前,实现转发的Serlvet不能有内容输出到客户端。如果在调用forward之前向Servlet引擎缓冲区中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法执行后,原来被输出到缓冲区中的内容将被清空;如果之前写入缓冲区的内容已经输出到客户端,那么调用forward时会抛出IllegalStateException异常;
(2)在调用forward方法之后,实现转发的Servlet会继续执行,直到它的逻辑完成,但是它的任何向客户端的输出都将被忽略;
(3)在调用者和被调用者程序中设置的响应状态吗和响应头都有效;
(4)如果调用者与被调用者访问的URL不属于同一目录,当被调用这输出到内容中包含使用相对URL的访问路径时,原来相对被调用者的URL将变成相对于调用者的URL。这是因为浏览器只直到当前访问的是调用者的URL,所以浏览器都已调用者作为参考,后面会使用图解释这种请求过程。
下面是一个示例,演示会演示上面提到的其中情况:
/**
* 调用者
*/
@WebServlet("/dispatch/ForwardDemo")
public class ForwardDemo extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
//在forward方法调用之前不能有内容输出到客户端
//如果在缓冲区中,则会被忽略
out.println("在调用forward方法之前");
//如果将缓冲区的内容强制输出到客户端,则会抛异常
//response.flushBuffer();
//设置属性,证明在被调用者中能获取到
request.setAttribute("content", "那时我们有梦");
RequestDispatcher dispatch = getServletContext().getRequestDispatcher("/Forwarded");
dispatch.forward(request, response);
//在forward方法之后的输出都将被忽略
out.println("在forward方法之后");
//但是调用者的逻辑也会正常执行,直到结束
System.out.println("虽然在forward之后,还是得到了执行");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
-----------------------------------------------------------
/**
* 被调用者程序
*/
@WebServlet("/Forwarded")
public class Forwarded extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
//获得在调用者程序中设置的属性
out.println(request.getAttribute("content") + "<br/>");
out.println("关于文学<br>关于爱情<br/>关于穿越世界的旅行<br/>"
+ "如今我们深夜饮酒<br/>杯子碰到一起都是梦破碎的声音<br/>");
RequestDispatcher dispatch = getServletContext().getRequestDispatcher("/html/dispatch.html");
dispatch.forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
在上面的实例中,其实被调用者也充当了调用者,所以它的内容实际上也不会被输出。注意关于相对路径的问题,现在在html路径下有两个文件:dispatch.html和Refresh.html,如果要在dispatch.html中使用链接指向Refresh.html,由于它们在同一个目录下那么应该这样写:
<a href="Refresh.html">刷新</a>
但是经过forward转发后,相对路径就不是dispatch.html所在的目录,而是以ForwardDemo(第一个调用者)的路径为相当目录了,如果想上面这样调用的话,浏览器会认为它访问的是这个路径:
http://localhost:8080/ServletJSP/dispatch/Refresh.html
所以应该这么调用:
<a href="../html/Refresh.html">必须以调用者的目录为相对路径</a>
运行上面的示例后,查看后台会发现有一条输出语句:“虽然在forward之后,还是得到了执行”,这说明调用者的逻辑还是会被正常执行。
HttpServletResponse.sendRedirect
sendRedirect(String url)也可以实现重定向,它会生成302状态码和Location相应头,通知客户端去重新访问Location响应头中指定的URL,可以是相对的URL也可以是绝对的URL,如果URL以"/"开头,则代表整个Web站点的根目录,不是Web应用的根目录,比如以Tomcat为例,"/"在这里表示:http://localhost:8080;如果不以“/”开头,则表示相对于当前请求的URL。sendRedirect与forward不同时,它可以重定向到其他站点。示例代码如下,跳转到Google主页:
/**
* ServletResponse.sendRedirect
*/
@WebServlet("/dispatch/RedirectServlet")
public class RedirectServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
//同样不能有输出,否则抛异常
out.println("before sendRedirect");
//response.flushBuffer();
//如果是Web应用中跳转,需要加上Web应用的路径
//response.sendRedirect(getServletContext().getContextPath() + "/html/Refresh.html");
//可以定位到其他站点
response.sendRedirect("http://www.google.com");
//后面的输出会被忽略,但是逻辑会完成
out.println("after sendRedirect");
System.out.println("after sendRedirect");
}
}
区别
RequestDispatcher.forward(resquest,response)和ServletRequest.sendRedirect(url)的区别:
(1)sendRedirect可以重定向到其他站点;
(2)最重要的区别是,他们的运行原理完全,首先看forward的运行过程:
(1)客户端发送请求,处理请求的是页面1;
(2)页面1发现需要跳转到页面2,于是在服务器端直接转发到页面2;
(3)页面2响应请求;
在整个过程中,浏览器地址栏不会发生改变,页面1和页面2是同一个请求和响应,也就是说reqeust和response是同一个。以上的过程如下图所示:
而sendRedirect的请求过程则是这样的:
(1)浏览器请求页面1;
(2)服务器发现需要转发到页面2,于是给浏览器发送302状态码,并然浏览器去访问Location头中的URL;
(3)浏览器请求页面2;
(4)页面2做出响应;
在上面的过程中,浏览器发生了两次请求,所以地址栏中的地址会发生改变,两次请求不会共享request,response,服务器会生成两对request和response,以上过程如下图所示:
以上介绍了Servlet中的重定向,JSP中有一套几乎相同的机制,其实JSP本身就是Servlet嘛。
转载请注明出处:喻红叶《Servlet中的重定向》
分享到:
相关推荐
JSPServlet的重定向技术综述 详细介绍
使用myEclipse + tomcat Servlet的简单例子,包括重定向,Session的简单使用 初学者使用......高手勿入 www.zhuyi123.cn助益信息网经常有新的信息 助益网
servlet请求转发、请求重定向、请求包含 获取form数据
本篇文章主要介绍了Servlet 实现重定向几种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
java servlet请求转发重定向 适合于初学者更好的了解页面跳转和原理
通过网上总结的Servlet的转发与重定向的区别介绍。希望可以帮的上大家
主要为大家详细介绍了servlet重定向的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
例:在servlet中重定向 public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { response.setContentType("text/html; charset=gb2312"); ...
javaServlet请求转发和重定向.pdf
这个是有关JSP-Servlet的重定向技术综述,希望对学习jsp和servlet的人有一些帮助!
USerLogin servlet 登录实例探究转发与重定向本质区别
所谓的重定向是将请求重新定个方向转到其他位置。例如,客户端访问AServlet,然后立刻自动访问BServlet。这个过程其实就是重定向。下面通过一张图来了解重定向,如图1-1所示:
jsp+servlet实现用户登录,可以了解到 jsp以及 servlet的重定向,以及servlet之间的重定向。
Servlet的执行流程也就是servlet的生命周期,当服务器启动的时候生命周期开始,然后通过init()《启动顺序根据web.xml里的startup-on-load来确定加载顺序》 方法初始化servlet,再根据不同请求调用doGet或doPost...
请求转发:在最终的 servlet (TestServlet) 中,request和中转的那个servlet(ForwardServlet...重定向:在最终的 servlet (TestServlet) 中,request和中转的那个servlet(SendServlet)中的request对象不是同一个对象
是在服务器端起作用, 当使用forward()时,Servlet engine传递HTTP请求从当前的Servlet or JSP到另外一个Servlet,JSP 或普通HTML文件,也即你的form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的...
7 Servlet:转发和重定向 8分 8 Servlet:连接池 5分 9 Servlet:servlet(代码题目) 6分 10 Servlet:jspuserbean 5分 11 Servlet:servlet(代码题目) 10分 12 Servlet:jstl标签(代码题目) 8分 13 Servlet:...
servlet实现简单登录验证,最简单的实现,通过equals()方法然后重定向,主要是理解servlet的运行机制
servlet+jsp练手小项目,适合初学者进行项目的练手,可以帮助我们熟悉servlet+jsp的项目流程,掌握转发,重定向,路径等问题,有助于后面框架的学习,而且项目并不复杂,主要是锻炼大家的思路,让我们可以熟悉servlet...
3. Servlet 在web.xml中的配置 10 4. Servlet元素说明 11 5. 如何写一个Servlet? 11 6. 在web.xml里配置的初始化参数怎么才能读回来? 12 7. 如何获得网页中form表单中的参数 12 8. session跨请求(页面)传数据怎么...