提起转发和重定向,我们脑海中就会浮现 request作用域;大家都知道当我们提交一个表单时,就创建了一个新
的请求。而实际上,当我们点击某个链接时,也会创建一个新的请求。那么一个请求的生命周期到底有多长呢,作用域会有多大?一起来看!
✎ 了解
现有一个页面 a.jsp,该页面中有一个链接:
<span style="font-size:18px;"><a href="b.jsp?id=1">这是指向b的一个链接,而且还带了一个参数</a></span>
当我们点击这个链接时,就会产生一个请求,我们将它称为requestA->B 。这个时候,我们就可以使用这个请求在b.jsp中获取数据信息 id 的信息了:
<span style="font-size:18px;">system.out.println(request.getParameter("id"))</span>
现在我们将难度升级,在b.jsp中添加如下语句:
<span style="font-size:18px;">request.setAttribute("name","funcreal");
system.out.println(request.getAttriblute("name"));//成功显示了name变量的值</span>
并且在b.jsp中添加链接:
<span style="font-size:18px;"><a href="c.jsp?age=23">这是指向c的一个链接,而且还带了一个参数</a></span>
当我们点击b.jsp的链接时,又会创建一个新的请求,而这个时候requestA->B 也就可以消匿了,所以我们将新的请求成为requestB->C ;那么我们可以从requestB->C中获取到age的数据信息,而id,name的属性我们就都无法在c.jsp中获取了,因为id和name都是requestA->B中的信息,而该请求已经消失了,所以无法获取这些信息;
那么转发又是几个意思呢??现在增加了一个页面 d.jsp ,并且在c.jsp的页面顶端加入一句:
<span style="font-size:18px;"><jsp:forward page="d.jsp"/></span><pre name="code" class="html"><span style="font-size:18px;"><%@ page contentType="text/html; charset=GBK" %>
<html>
<body bgcolor="#ffffff">
<%
out.println("id=" + request.getParameter("id"));
out.println("name=" + request.getAttribute("name"));
out.println("age=" + request.getParameter("age"));
%>
</body>
</html></span>
d.jsp的页面如下:
<span style="font-size:18px;"><%@ page contentType="text/html; charset=GBK" %>
<html>
<body bgcolor="#ffffff">
requestB-C的魔爪已经伸到了d.jsp页面
<%
out.println("age=" + request.getParameter("age"));
%>
</body>
</html></span>
运行程序,我们可以发现,c.jsp的内容并没有显示出来,因为forward是自动执行的,地址中虽然是c.jsp,但实际上浏览器中显示的已经是d.jsp的内容,而且可以看到从b.jsp中传过来的参数;所以我们可以这样理解:转发,延长了requestB->C的作用域,而c.jsp顶端添加的那句代码:<jsp:forwardpage="d.jsp"/>,则是将c.jsp和d.jsp粘连到了一起,它们就相当于处于同一个页面中。比如在struts中的Action,我们经常会用到一句话:mapping.findForward("###****"),因为我们在Action设置中的请求作用域的变量都会被应用到下一个页面(或者Action)中,所以使用的是转发;
✎ 比较 RequestDispatcher.forward 和 HttpServletResponse.sendRedirect:
(1)forward 方法只能将请求转发给同一个WEB应用中的组件;而sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
如果 传递给sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher 对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。
(2)调用sendRedirect 方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;而调用 forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。
(3)sendRedirect 方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求。
举个例子:重定向过程好比有个绰号叫“浏览器”的人写信找张三借钱,张三回信说没有钱,让“浏览器”去找李四借, 并将李四现在的通信地址告诉给了“浏览器 ”。于是,“浏览器”又按张三提供通信地址给李四写信借钱,李四收到信后就把钱汇给了“浏览器”。可见,“浏览器”一共发出了两封信和收到了两次回复,“ 浏览器”也知道他借到的钱出自李四之手。
forward 方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。这个过程好比绰号叫“ 浏览器”的人写信找张三借钱,张三没有钱,于是张三找李四借了一些钱,甚至还可以加上自己的一些钱,然后再将这些钱汇给了“浏览器”。可见,“浏览器”只 发出了一封信和收到了一次回复,他只知道从张三那里借到了钱,并不知道有一部分钱出自李四之手。
(4)forward 方法的调用者与被调用者之间共享相同的request 对象和response 对象,它们属于同一个访问请求和响应过程;而sendRedirect 方法调用者与被调用者使用各自的request 对象和response 对象,它们属于两个独立的访问请求和响应过程。
对于同一个WEB应用程序的内部资源之间的跳转,特别是跳转之前要对请求进行一些前期预处理,并使用 HttpServletRequest.setAttribute 方法传递预处理结果,那就应该使用 forward 方法。
不同WEB应用程序之间的重定向,特别是要重定向到另外一个WEB站点上的资源的情况,都应该 sendRedirect方法。
(5)无论是forward 方法,还是sendRedirect 方法,在调用它们之前,都不能有内容已经被实际输出到了客户端。如果缓冲区中已经有了一些内容,这些内容将被从缓冲区中清除。
✎ 总结:
当使用转发时,JSP容器将使用一个内部的方法来调用目标页面,新的页面继续处理同一个请求,而浏览器将不会知道这个过程。
与之相反,重定向方式的含义是第一个页面通知浏览器发送一个新的页面请求。因为,当你使用重定向时,浏览器中所显示的URL会变成新页面的URL, 而当使用转发时,该URL会保持不变。由于重定向需要浏览创建一个新的请求,所以速度要比转发慢。同时,由于重定向方式产生了一个新的请求,所以经过一次重 定向后,request内的对象将无法使用。
怎么选择是重定向还是转发呢?通常情况下转发更快,而且能保持request内的对象,所以它是第一选择。但是由于在转发之后,浏览器中URL仍然指向开始页面,此时如果重载当前页面,开始页面将会被重新调用。