Web应用在响应客户端的一个请求时,有可能响应过程很复杂,需要多个Web组件共同协作,才能生成响应结果。此时,可以使用转发技术。
转发的理解:在同一个web应用内部,一个组件将未完成的任务转发给另外一个组件,由另外一个组件进行后续的处理并生成响应结果。
最常见的情况是一个Servlet完成了业务逻辑的处理,将数据展现交给一个JSP来完成。
转发的编程:
1. request.setAttribute(String name,Object obj) //绑定要转发的数据 2. request.getRequestDispatcher(String path).forward(request,response) //转发 3. request.getAttribute(String name) //在另外一个组件中取出前一个web组件带过来的数据转发的特点:
1. 一件事情未完成 2. 转发的目的地,只能是同一个应用内部 3. 转发时,浏览器地址栏的地址没有发生变化 4. 转发涉及的组件之间,可以共享同一个request和response.
下面简单地以两个Servlet组件之间的转发为例:
CheckServlet转发到OutputServlet,为两个Servlet映射的url分别为check和output,代码如下:
public class CheckServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username=request.getParameter("username"); String message=null; if(username==null){ message="please input username"; }else{ message="hello,"+username; } request.setAttribute("msg",message);//绑定数据 PrintWriter pw=response.getWriter(); RequestDispatcher rd=request.getRequestDispatcher("/output");//得到转发器 pw.println("output from CheckServlet before forwarding request"); System.out.println("output from CheckServlet before forwarding request"); rd.forward(request, response); pw.println("output from CheckServlet after forwarding request"); System.out.println("output from CheckServlet after forwarding request"); } }
public class OutputServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String message=(String)request.getAttribute("msg"); PrintWriter pw=response.getWriter(); pw.println(message); pw.close(); } }在浏览器地址栏里输入:http://localhost:8080/dispatcher/check,回车,得到:
输出的内容只有please input username。这就是说,CheckServlet里的
pw.println("output from CheckServlet before forwarding request"); rd.forward(request, response); pw.println("output from CheckServlet after forwarding request");pw所生成的响应是不会发送到客户端的。即只有最后的目标组件生成的响应才会被发送到客户端。
我们再来看看CheckServlet里的这部分代码:
pw.println("output from CheckServlet before forwarding request"); System.out.println("output from CheckServlet before forwarding request"); rd.forward(request, response); pw.println("output from CheckServlet after forwarding request"); System.out.println("output from CheckServlet after forwarding request");
我们同时看看刚才的访问时控制台输出的内容:
所以可以知道,源组件CheckServlet在rd.forward(request,response)之后的代码也会被执行。事实上,这一点和重定向是一致的。重定向中,在response.sendRedirect(String location)之后的代码也会被执行。
我们来看看rd.forward(request,response)方法的处理流程:
1. 清空用于存放响应正文数据的缓冲区 2. 如果目标组件为Servlet或JSP,就调用他们的service方法,把该方法产生的响应结果发送到客户端;如果目标组件为文件系统中的静态html文件,就读取文档中的数据并把它发送到客户端。从rd.forward(request,response)方法的处理流程可以看出,转发具有以下特点:
1. 由于forward方法先清空用于存放响应正文数据的缓冲区,因此Servlet源组件生成的响应结果不会被发送到客户端,只有目标组件生成的响应结果才会被发送到客户端。 2. 如果源组件在进行转发之前,已经提交了响应结果(例如调用ServletResponse的flushBuffer()方法,或者调用与ServletResponse关联的输出流的close()方法),那么forward方法会抛出IllegalStateException.为了避免该异常,不应该在源组件中提交响应结果。