Servlet的学习之Request请求对象(3)
本篇接上一篇,将Servlet中的HttpServletRequest对象获取RequestDispatcher对象后能进行的【转发】forward功能和【包含】include功能介绍完。
首先来看RequestDispatcher对象的“转发”功能:
在《Servlet的学习(五)》中说过,使用ServletContext对象的getRequestDispatcher方法可以获得转发对象RequestDispatcher对象,将请求进行转发给其他的Servlet或者JSP处理,同时在该篇的结尾,也注明了其实使用ServletContext的方式不适合实际开发,因为ServletContext对象是web域对象,在web应用部署的整个生命周期内,是作为所有Servlet的共同拥有者,这就导致了在多线程情况下会引起ServletContext对象中用于转发的数据产生线程安全问题:一个Servlet利用ServletContext在转发给JSP过程中,而另一个线程中的Servlet使用ServletContext将这个转发的数据给覆盖,这样导致原先该转发给JSP的数据并不是我们期待的。
而Request请求对象也是域对象,虽然这个域对象的生命周期并没有ServletContext那么长,但是如果采用Request对象的转发还是能持续到别的Servlet处理时的。使用Request请求对象来处理转发的最大的好处就是在多线程中,每一个线程都有自己的Request请求对象,这就不会像ServletContext对象一样是线程所共有的,因此不会产生线程安全问题。
那么我们将《Servlet的学习(五)》中使用ServletContext对象来转发的例子改为使用HttpServletRequest请求对象来转发:
在【myservlet】web工程下创建一个名为“ServletRequest1”的Servlet和一个show.jsp
在ServletRequest1中将数据转发给show.jsp,代码为:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { String data = "Ding love LRR";
request.setAttribute("data", data);
RequestDispatcher dispatcher = request.getRequestDispatcher("/show.jsp");
dispatcher.forward(request, response);
}
而在show.jsp中接收这个数据(属性),并封装在HTML中:
<font size="100px" color="red">
<%
String data = (String)request.getAttribute("data");
out.write(data);
%>
</font>
在浏览器中访问这个Servlet,会看到:

当然,还是跟之前一样,转发功能不会使浏览器的地址发生变化。
最后再说一句,转发功能之所以这么重要,主要原因在于在MVC设计模式下,都是Servlet处理用户请求的,并产生用户想看的数据,然后转交给JSP显示,在转交给JSP显示时,会把数据存在Request域对象里带给JSP。
转发功能还有一些要注意的地方:
如果在调用RequestDispatcher对象的forward方法之前,将Servlet程序通过Response响应对象写出数据之后如果关闭流或刷新流,这是将缓存的数据发送给了客户端,相当于向客户端提交了数据,那么再使用Request请求对象来转发将会抛出IllegalStateException异常:
如我们在上述Servlet中的代码前加入向该Servlet写入数据:
PrintWriter writer = response.getWriter();
writer.write("Long live sd");
writer.close(); //或writer.flush(),这步相当于向客户端提交了响应 request.setAttribute("data", "Ding love LRR");
RequestDispatcher dispatcher = request.getRequestDispatcher("/show.jsp");
dispatcher.forward(request, response);
那么我们在浏览器将只能访问响应提交的数据,而不能看到转发后的页面:

同时服务器后台报错,抛出IllegalStateException异常:

从提示也可以看出,在转发之前不能提交Response响应(Cannot forward after response has been committed)。
而如果我们将Response的输出流不关闭,或者放最后关闭,则可以看到转发还是成功的:
PrintWriter writer = response.getWriter();
writer.write("Long live sd"); request.setAttribute("data", "Ding love LRR");
RequestDispatcher dispatcher = request.getRequestDispatcher("/show.jsp");
dispatcher.forward(request, response); writer.close(); //response的输出流在转发之后再关闭
可以访问到要转发的页面(show.jsp代码在上面),同时服务器后台也不报错:

这是因为通过write方法写入的数据还在Response对象的缓冲区中,我们知道如果使用了close()或flush()都会将缓存的数据立马真正输出,那就相当于已经想客户端提交了响应,之后如果再转发肯定会抛出异常。而如果我们没有输出数据(没有提交响应),那么转发功能正常,而原先通过Response响应对象写入缓冲区中的数据将会被清空,但是通过设置响应头字段信息的内容将保持有效(例如在转发之前写的response.setContentType方法等)。
另外,如果我们在转发之后再继续通过Response对象写出数据,也是没有用的,因为转发就相当于已经将请求和响应提交给客户端了,看到的还是转发的页面,不过这时候服务器后台不会报错就是了。
总结,在第一次【转发】之前或之后向客户端写出数据或者再次转发都是不合理的。建议在转发语句后直接跟一个return是非常理智的选择。
另外,如果在转发一次之后再转发还是会发生IllegalStateException异常,因为转发一次就相当于向服务器提交一次Response响应了:
request.getRequestDispatcher("/index.jsp").forward(request, response);
request.getRequestDispatcher("/1.html").forward(request, response);
在浏览器中只能看到最开始转发的那个页面:

同时服务器后台抛出IllegalStateException异常:

这种问题在使用if条件语句时要特别注意。
==========================================================================
接下来看RequestDispatcher对象的“包含”功能:
RequestDispatcher对象的include方法用于将一些web资源(如Servlet,JSP,HTML等)包含在Response响应对象中。
举个例子,在【myservlet】中创建一个【public】文件夹,里面再创建“header.html”和“footer.html”,另外创建一个Servlet
在header.html页面中代码为:

在footer.html页面中代码为:

而在Servlet中代码为:
request.getRequestDispatcher("public/header.html").include(request, response);
response.getWriter().write("long live sd <br />");
request.getRequestDispatcher("public/footer.html").include(request, response);
访问该Servlet后发现:

原来响应写入HTML是使用OutputStream,与JSP不同()(《Servlet的学习之Response响应对象(3)》)
那么代码只好改为:
request.getRequestDispatcher("/public/header.html").include(request, response);
response.getOutputStream().write("long live sd <br />".getBytes());
request.getRequestDispatcher("/public/footer.html").include(request, response);
那么再次访问该Servlet:

注意:被包含的Servlet(如上例中的Servlet)不能改变响应对象中的响应头和响应状态码,如果在包含的Servlet中含有这样企图更改的句子,则会被服务器忽略。
==========================================================================
最后来总结下RequestDispatcher的forward转发和Response的sendRedirect重定向的区别:
1,forward转发只能将请求转发给该web应用中的web资源。
sendRedirect重定向除了能重定向到该web应用中的资源外,还能重定向到同一个主机(或服务器)上的其他web应用中的资源,甚至可以使用绝对URL重定向到其他主机(或服务器)上得资源。
2,创建RequestDispatcher对象时指定的相对URL以“/”开头,相当于当前web应用程序的根目录,也就是说在该“/”之后代表当前web应用所在目录下的内容,例如:
request.getRequestDispatcher("/index.jsp")
如果是调用HttpServletResponse.sendRedirect方法的相对URL以“/”开头,则是相对整个服务器存放web应用的根目录,例如以Tomcat的【webapps】目录为重定向目录的根目录,如:response.sendRedirect(“/myservlet/index.jsp”),其中”myservlet”是web工程名,同时也是web应用所在目录名,存放于Tomcat的【webapps】目录下。
3,调用RequestDispatcher对象的转发forward方法进行转发过程结束后,浏览器地址栏中的URL地址不会发生改变。
而调用HttpServletResponse.sendRedirect重定向方法则在访问过程结束后,浏览器地址栏中的URL地址将会变成重定向资源的路径。
4,RequestDispatcher对象的转发forward方法的调用者和被调用者共享相同的request请求对象和response响应对象,它们属于同一个访问请求和响应过程。
而HttpServletResponse.sendRedirect重定向方法的调用者和被调用者使用各自的request请求对象和response响应对象,它们属于两个独立的访问请求和响应过程。
有一个例子可以比较形象的说明转发和重定向的不同:
转发:A向B借钱,B说没有,但是B帮A向C借钱。
重定向:A向B借钱,B说没有,B让A重新向C借钱。
Servlet的学习之Request请求对象(3)的更多相关文章
- Servlet的学习之Request请求对象(2)
在上一篇<Servlet的学习(十)>中介绍了HttpServletRequest请求对象的一些常用方法,而从这篇起开始介绍和学习HttpServletRequest的常用功能. 使用Ht ...
- Servlet的学习之Request请求对象(1)
在本篇中开始对Servlet中的HttpServletRequest请求对象进行学习,请求对象同响应对象一样,我们可以根据该对象中的方法获取例如请求行,请求头和请求实体数据的方法. 在本篇中先对Htt ...
- Servlet的学习之Response响应对象(3)
本篇来说明响应对象HttpServletResponse对象的最后一点内容. 首先来看响应对象控制浏览器定时刷新,在我的web应用[myservlet]中创建Servlet,在该Servlet中设置响 ...
- Servlet的学习之Response响应对象(2)
本篇接上一篇<Servlet的学习之Response响应对象(1)>,继续从HttpServletResponse响应对象来介绍其方法和功能. 使用setHeader方法结合HTTP协议的 ...
- JSP内置九个对象Request请求对象
jsp内置对象是什么呢? 例如Java语言使用一个对象之前需要实例化(也就是所说的new一个对象),创建对象这个过程有点麻烦,所以在jsp中提供了一些内置对象,用来实现很多jsp应用.在使用内置对象时 ...
- opa gatekeeper笔记:AdmissionReview input.request请求对象结构
官方:https://v1-17.docs.kubernetes.io/zh/docs/reference/access-authn-authz/extensible-admission-contro ...
- Servlet的学习之Response响应对象(1)
在之前学习了Servlet中的主体结构,包括Servlet的生命周期方法,和非生命周期方法能获取的一些非常重要的对象如ServletConfig.ServletContext对象等,而从这篇开始我们将 ...
- request请求对象实例
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="DemoRequest.as ...
- FastAPI(54)- 详解 Request 请求对象
背景 前面讲了可以自定义 Response,那么这里就讲下请求对象 Request 可以通过 Request 来获取一些数据 获取请求基础信息 @app.get("/base") ...
随机推荐
- SSH整合,"sessionFactory " or "hibernateTemplate " is required异常
首先遇到的问题就是HibernateDaoSupport引起的,程序中所有的DAO都继承自HibernateDaoSupport,而HibernateDaoSupport需要注入sessionfact ...
- Stackful 协程库 libgo(单机100万协程)
libgo 是一个使用 C++ 编写的协作式调度的stackful协程库, 同时也是一个强大的并行编程库. 设计之初是为高并发分布式Linux服务端程序开发提供底层框架支持,可以让链接进程序的同步的第 ...
- 安卓Launcher之获取手机安装的应用列表,安卓launcher
Launcher中最主要的就是获取所有应用列表的入口以及图标,一般获取的方法有两种: PackageInfo ResolveInfo 运行获取所有APP的Launcher并且允许进行点击事件,进入到应 ...
- C#委托好处知多少
1.性能 性能是泛型的一个主要优点. 直接上例子,通过实例可以让我们很好的理解这一点. Stopwatch stopwatch = new Stopwatch(); stopwatch.Start() ...
- 无线网络wifi (WPA/WPA2)密码破解方法
无线网络password破解WPA/WPA2教程 本教程用于探索无线路由安全漏洞,禁止用于非法用途,违者法律必究(与我无关) 在动手破解WPA/WPA2前,应该先了解一下基础知识,本文适合新手阅读 首 ...
- 再造 “手机QQ” 侧滑菜单(二)——高仿左视图
代码示例:https://github.com/johnlui/SwiftSideslipLikeQQ 本篇文章中,我们将一起使用 Auto Layout 高仿手Q的左侧视图,力争达成从布局到动画的全 ...
- 将EC2里的实例导出到RAW文件并进行修改
你可能有自己的instance在amazon云环境里面,或者是你想深度修改一下marketplace里面提供的那些系统又估计运行中的instance改动不方便 亚马逊作为云计算领域的大哥大,我不得不说 ...
- SQL SERVER 2008R2sp1配置Database Mail –用SQL 数据库发邮件
步骤1)创建配置文件和帐户 看图片吧,挺简单的: 中间略过的一些步骤,就点下一步即可. 下面我们测试一下: Step 2)配置邮件: 在完成账户和配置文件创建之后,我们需要配置Database Mai ...
- jsp生命周期和工作原理
jsp的工作原理jsp是一种Servlet,但是与HttpServlet的工作方式不太一样.httpservlet是先由源代码编译为class文件后部署到服务器下的,先编译后部署.而jsp则是先部署后 ...
- python pyfits
原链接:http://blog.sina.com.cn/s/blog_a046022d0101a48h.html,全文复制以备出错. 导入pyfits模块:import pyfits ...