Servlet分析(5)请求转发

  • Post author:
  • Post category:其他




请求转发


生活中,110报警中心收到群众报警电话,根据报警的内容(报警地点、事情紧急程度),

将报警请求交由不同的派出所进行处理

。在这里,110报警中心充当了一个调度员的角色,它负责将各种报警请求转发给实际的处理单位。这种处理模型的好处是:

1)给人们提供了统一的报警方式(拨打110)。

2)报警中心可以根据报案人所处的位置、派出所的地理位置与人员状况,合理调度资源,安排就近的派出所及时出警。

3)报警中心并不处理具体的案件,缩短了对报警请求的响应时间。

在Web应用中,这种处理模型也得到了广泛的应用,这种调度员的角色通常由Servlet来充当,

这样的Servlet叫做控制器(Controller)

。在控制器中,可以将请求转发(request dispatching)给另外一个Servlet或者JSP页面,甚至是静态的HTML页面,然后由它们进行处理并产生对请求的响应。

要完成请求转发,就要用到javax.servlet.RequestDispatcher接口。



RequestDispatcher接口

RequestDispatcher对象由Servlet容器创建,用于封装一个由路径所标识的服务器资源。利用RequestDispatcher对象,可以把请求转发给其他的Servlet或JSP页面。在RequestDispatcher接口中定义了两种方法。



  1. public




    void


    forward(ServletRequest request, ServletResponse response)


    throws


    ServletException, java.io.IOException


该方法用于将请求从一个Servlet传递给服务器上的另外的Servlet、JSP页面或者是HTML文件。


在Servlet中,可以对请求做一个初步的处理,然后调用这个方法,将请求传递给其他的资源来输出响应。


要注意的是,这个方法必须在响应被提交给客户端之前调用,否则的话,它将抛出IllegalStateException异常。在forward()方法调用之后,原先在响应缓存中的没有提交的内容将被自动清除。



  1. public




    void


    include(ServletRequest request, ServletResponse response)


    throws


    ServletException, java.io.IOException




该方法用于在响应中包含其他资源(Servlet、JSP页面或HTML文件)的内容。和forward()方法的区别在于:利用include()方法将请求转发给其他的Servlet,被调用的Servlet对该请求做出的响应将并入原先的响应对象中,原先的Servlet还可以继续输出响应信息。而利用forward()方法将请求转发给其他的Servlet,将由被调用的Servlet负责对请求做出响应,而原先Servlet的执行则终止。



获取RequestDispatcher对象

有三种方法可以用来获取RequestDispatcher对象。一是利用ServletRequest接口中的getRequestDispatcher()方法:



  1. public


    RequestDispatcher getRequestDispatcher(java.lang.String path)

另外两种是利用ServletContext接口中的getNamedDispatcher()和getRequestDispatcher()方法:



  1. public


    RequestDispatcher getRequestDispatcher(java.lang.String path)



  2. public


    RequestDispatcher getNamedDispatcher(java.lang.String name)



可以看到ServletRequest接口和ServletContext接口各自提供了一个同名的方法getRequestDispatcher(),那么这两个方法有什么区别呢?

两个getRequestDispatcher()方法的参数都是资源的路径名,不过ServletContext接口中的getRequestDispatcher()方法的参数必须以斜杠(/)开始,被解释为相对于当前上下文根(context root)的路径。例如:/myservlet是合法的路径,而../myservlet是不合法的路径;而ServletRequest接口中的getRequestDispatcher()方法的参数不但可以是相对于上下文根的路径,而且可以是相对于当前Servlet的路径。例如:/myservlet和myservlet都是合法的路径,如果路径以斜杠(/)开始,则被解释为相对于当前上下文根的路径;如果路径没有以斜杠(/)开始,则被解释为相对于当前Servlet的路径。ServletContext接口中的getNamedDispatcher()方法则是以在部署描述符中给出的Servlet(或JSP页面)的名字作为参数。

调用ServletContext对象的getContext()方法可以获取另一个Web应用程序的上下文对象,利用该上下文对象调用getRequestDispatcher()方法得到的RequestDispatcher对象,可以将请求转发到另一个Web应用程序中的资源。但要注意的是,要跨Web应用程序访问资源,需要在当前Web应用程序的<context>元素的设置中,指定crossContext属性的值为true。



sendRedirect()和forward()方法的区别

HttpServletResponse接口的sendRedirect()方法和RequestDispatcher接口的forward()方法都可以利用另外的资源(Servlet、JSP页面或HTLM文件)来为客户端进行服务,但是这两种方法有着本质上的区别。下面分别给出sendRedirectt()方法和forward()方法的工作原理。

sendRedirect()方法的工作原理/交互过程如下:

1)浏览器访问Servlet1。

2)Servlet1想让Servlet2为客户端服务。

3)Servlet1调用sendRedirect()方法,将客户端的请求重定向到Servlet2。

4)浏览器访问Servlet2。

5)Servlet2对客户端的请求做出响应。

从上述交互过程可以看出,调用sendRedirect()方法,

实际上是告诉浏览器Servlet2所在的位置

,让浏览器重新访问Servlet2。调用sendRedirect()方法,会在响应中设置Location响应报头。要注意的是,这个过程对于用户来说是透明的,浏览器会自动完成新的访问。

浏览器网址栏显示的URL是重定向之后的URL。

forward()方法的工作原理/交互过程如下:

1)浏览器访问Servlet1。

2)Servlet1想让Servlet2对客户端的请求进行响应,于是调用forward()方法,将

请求转发

给Servlet2进行处理。

3)Servlet2对请求做出响应。

从上述交互过程可以看出,调用forward()方法,对浏览器来说是透明的,浏览器并不知道为其服务的Servlet已经换成Servlet2了,

它只知道发出了一个请求,获得了一个响应。浏览器网址栏显示的URL始终是原始请求的URL。

sendRedirect()方法和forward()方法还有一个区别,那就是sendRedirect()方法不但可以在位于同一主机上的不同Web应用程序之间进行重定向,而且可以将客户端重定向到其他服务器上的Web应用程序资源。



六、重定向与转发





(1)什么是重定向?




服务器向浏览器发送一个302状态码及一个Location消息头(该消息头的值是一个地址,称之为重定向地址),浏览器会立即向重定向地址发请求。




(2)什么是转发?




一个web组件(servlet/jsp)将未完成的处理通过容器转交给另外一个web组件继续完成。




最常见的情况是:一个servlet获得数据,然后转发给一个jsp,由这个jsp来生成相应的页面。




(3)如何重定向?




response.sendRedirect(String url);




(4)如何转发?




step1, 先绑订数据到request对象上。




//绑订数据到request对象上,name:绑订名称 obj:绑订值。




request.setAttribute(String name,Object obj);




//依据绑订名称找到绑订值,如果找不到,返回null。




Object request.getAttribute(String name);




step2,获得一个转发器




//url:转交给哪一个web组件,一般是一个jsp。//RequestDispatcher:是一个接口。




RequestDispatcher rd =  request.getRequestDispatcher(String url);




step3,转发




//

转发所涉及的每个web组件都会共享同一个request,response。





rd.forward(request,response);




(5)编程需要注意的问题




a,重定向和转发之前,不能够调用out.close方法。




b,重定向和转发之前,会清空response对象中缓存的数据。




(6)特点




a,重定向的地址任意。转发的目的地只能够是同一个应用内部的某个组件的地址。




b,重定向之后,浏览器地址栏的地址会变成重定向地址。转发之后,浏览器地址栏的地址没有变化。






小知识:常见的错误处理




1)404




404是一个状态码(服务器发送给浏览器的一个三位数字,表示服务器处理的状态),404表示依据请求资源路径找不到对应的资源。




解决办法: 依据


http://ip:port/appname/url-pattern


检查请求资源路径。




2)500




500也是一个状态码,表示系统错误。




解决办法:检查源代码以及配置文件。




3)405




表示找不到对应的方法来处理请求。




解决办法:检查service方法的签名。







七、servlet线程安全问题





(1)为什么说servlet会有线程安全问题?




a,servlet

只有一个实例

,即不管有多少个请求,容器只会创建一个servlet实例。




b,当容器收到一个请求之后,会启动一个线程来处理请求,该线程会调用servlet实例的方法。




如果有多个线程都在访问同一个servlet实例的方法,该方法有线程不安全的代码(比如,该方法在修改servlet实例的属性)就有可能发生线程安全问题。




(2)如何解决?




a,加锁




可以使用synchronized对整个service方法加锁,也可以对具有线程安全问题的代码块加锁。




b,可以让servlet实现SingleThreadModel接口。容器会为每一个线程分配一个servlet实例,但是,会产生过多的servlet实例,不建议使用。