1.RequestDispatcher
RequestDispatcher对象可以通过ServletContext对象的getRequestDispatcher方法获取,如下。
public abstract RequestDispatcher getRequestDispatcher(String paramString); public abstract RequestDispatcher getNamedDispatcher(String paramString);
关于RequestDispatcher的源代码(源代码反编译自apache-tomcat-6.0.16下的servlet-api.jar)如下所示:
package javax.servlet; import java.io.IOException; public abstract interface RequestDispatcher { public abstract void forward(ServletRequest paramServletRequest, ServletResponse paramServletResponse) throws javax.servlet.ServletException, IOException; public abstract void include(ServletRequest paramServletRequest, ServletResponse paramServletResponse) throws javax.servlet.ServletException, IOException; }
1.1 include
include方法的使用示例如下:
package org.it315.ch5.inc; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class IncludingServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=gb2312"); // response.setContentType("text/html;charset=iso8859-1"); PrintWriter out = response.getWriter(); String china = "中国"; out.println("china:" + china + "<br>"); RequestDispatcher rd = getServletContext().getRequestDispatcher( "/IncludedServlet?p1=" + china); out.println("start including:" + "<br>"); rd.include(request, response); out.println("end including" + "<br>"); } }
被引入(include)的servlet代码如下:
package org.it315.ch5.inc; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class IncludedServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // response.setContentType("text/html;charset=GB2312"); // response.setContentType("text/html;charset=iso8859-1"); // response.setCharacterEncoding("GB2312"); // response.setCharacterEncoding("iso8859-1"); response.setContentType("text/html;charset=GB2312");//由于被IncludingServlet所include,所以不在起作用 PrintWriter out = response.getWriter(); out.println("中国" + "<br>"); out.println("URI:(这里的request应该是调用者的request)" + request.getRequestURI() + "<br>"); out.println("QueryString(**注意这里**):" + request.getQueryString() + "<br>"); out.println("parameter p1:" + request.getParameter("p1") + "<br>"); } }
对于的web.xml配置如下:
<!-- begin --> <servlet> <servlet-name>IncludedServlet</servlet-name> <servlet-class>org.it315.ch5.inc.IncludedServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>IncludedServlet</servlet-name> <url-pattern>/ch5/IncludedServlet</url-pattern> </servlet-mapping> <!--end --> <!-- begin --> <servlet> <servlet-name>IncludingServlet</servlet-name> <servlet-class>org.it315.ch5.inc.IncludingServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>IncludingServlet</servlet-name> <url-pattern>/ch5/IncludingServlet</url-pattern> </servlet-mapping> <!--end -->
页面通过以下html访问即可。
<a href="IncludingServlet" >including,此时被引入的servlet——IncludedServlet并获得不到参数。</a> <br/> <a href="IncludingServlet?p1=test测试" >including传参数,此时被引入的servlet类IncludedServlet可以获得到参数</a>
分析:
为什么调用IncludingServlet,并在IncludingServlet中加入
RequestDispatcher rd = getServletContext().getRequestDispatcher( "/IncludedServlet?p1=" + china);
确在IncludingServlet中确得不到参数呢??(这也命中注定“<jsp:include page="../common/nav.jsp?flag=1" />”这种方式传递参数是不可行的)
这是因为: includingServlet 和IncludingServlet使用的是一个HttpServletRequest对象和一个IncludedServlet对象。上面的方法并没有重新创建一个Request请求,虽然他的外表极为相像。
1.2 forward
mvc控制器所仰仗的就是该方法,学习了forward和过滤器,相信就可以实现自己的mvc了。
forward的使用示例代码如下:
调用者
package org.it315.ch5.forward; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ForwardingServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); // 禁止浏览器缓存,以免影响实验效果 response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); PrintWriter out = response.getWriter(); // 下面这些输出,将会在调用rd.forward(request, response);方法后被web容器清空 out.println("before forward");// 这些输错 // 用于将缓冲区中的内容强制刷新到客户端,如果调用了rd.forward(request, response)则抛出异常。 // response.flushBuffer(); String china = "中国"; // 不同于rd.include(request, response),该参数值可以成功的传递过去。另外,这里是ch5开头的,是从web应用路径径算起的。 RequestDispatcher rd = getServletContext().getRequestDispatcher( "/ch5/ForwardedServlet?p1=" + java.net.URLEncoder.encode(china, "GB2312")); /** * 与rd.include(request, response)不同,调用rd.forward(request, response)方法后, * web容器首先会清空“响应体内容” */ rd.forward(request, response); // 由于之前调用了rd.forward(request, response)方法,下面的输出将会被忽略 out.println("after forwarding"); System.out.println("after forwarding"); } }
被调用者
package org.it315.ch5.forward; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ForwardedServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /*** * 与被include不同,被forward时,下面的response.setContentType将会覆盖调用者的response. * setContentType。 * 不过前提是,在调用setContentType()方法之前没有调用response.getWriter()方法。 */ response.setContentType("text/html;charset=GB2312"); PrintWriter out = response.getWriter(); /* * String basePath = request.getScheme() + "://"+ * request.getServerName() + ":" + request.getServerPort() + * request.getRequestURI(); out.println("<base href=\"" + basePath + * "\">"); */ out.println("<a href='ForwardedServlet'>访问自己</a><br>"); out.println("URI:" + request.getRequestURI() + "<br>"); out.println("QueryString:" + request.getQueryString() + "<br>"); out.println("URL:" + request.getRequestURL() + "<br>"); String p1 = request.getParameter("p1"); String chP1 = null; if (p1 != null) { chP1 = new String(p1.getBytes("iso8859-1"), "GB2312"); } out.println("parameter p1:" + chP1 + "<br>"); } }
对应的web.xml配置如下:
<!--ForwardTest begin --> <servlet> <servlet-name>ForwardingServlet</servlet-name> <servlet-class>org.it315.ch5.forward.ForwardingServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ForwardingServlet</servlet-name> <url-pattern>/ch5/ForwardingServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>ForwardedServlet</servlet-name> <servlet-class>org.it315.ch5.forward.ForwardedServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ForwardedServlet</servlet-name> <url-pattern>/ch5/ForwardedServlet</url-pattern> </servlet-mapping> <!--ForwardTest end -->
访问链接:http://localhost:8080/Test_servlet/ForwardedServlet
1.3 include和forward的区别
1.3.1 相同点
a.这两个方法都来自RequestDispatcher 类(嘿嘿,被人抽)
b.这两个方法都是在服务端执行,这不同于HttpServletResponse中的sendRedirect(String paramString)方法(sendRedirect方法是通过发送“消息头”,让浏览器去重新指定的)。
c.调用者和被调用者用的都是一组HttpServletResponse和HttpServletRequest对象,并未创建新的对象。
d.访问路径没有变化
e.都只能访问当前web应用中的资源,不同于HttpServletResponse中的sendRedirect(String paramString)。
1.3.2不同点
a.include将是被调用的页面和调用的页面显示在一起;而forward后,只会显示被调用者的页面。
b.include不会传递在 getRequestDispatcher 方法中添加的参数,如下:
RequestDispatcher rd = getServletContext().getRequestDispatcher( "/*Servlet?p1=" + china);
不过你可以通过在调用者上加入参数以达到目的,前面有讲;而forward可以通过getRequestDispatcher 方法传递参数。
c.include中,被调用的servlet将调用response.setContentType("text/html;charset=GB2312");等方法将不会起作用。forward中,被调用的servlet将调用response.setContentType("text/html;charset=GB2312");将会冲掉调用者servlet的设置(前提是调用者中没有使用response.getWriter();)。
d.实际上,include中不调用PrintWriter out = response.getWriter();是一个很诧异的行为;而在forward中调用PrintWriter out = response.getWriter();确实一个很250的行为,毕竟forward主要用于跳转。
e.访问路径差别,对于代码
RequestDispatcher rd = getServletContext().getRequestDispatcher( "/ch5/testServlet);
foward访问的是: web应用路径/ch5/testServlet。即:
而include访问的路径则是:当前路径/ch5/testServlet。例如,如果调用者本身的路径为“http://localhost:8080/Test_servlet/ch5/IncludingServlet”。那么访问的路径将等同为:
参考:《深入体验Java_Web开发内幕-核心基础\》
……