国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Servlet/JSP深入詳解:基于Tomcat的Web開發(fā)--第二章03

2.3  Servlet異常

在javax.servlet包中定義了兩個異常類,ServletException和UnavailableException。

2.3.1  ServletException類

ServletException類定義了一個通用的異常,可以被init()、service()和doXXX()方法拋出,這個類提供了下面4個構(gòu)造方法和1個實例方法:

Ø  public ServletException()

該方法構(gòu)造一個新的Servlet異常。

Ø  public ServletException(java.lang.String message)

該方法用指定的的消息構(gòu)造一個新的Servlet異常。這個消息可以被寫入服務(wù)器的日志中,或者顯示給用戶。

Ø  public ServletException(java.lang.String message, java.lang.Throwable rootCause)

在Servlet執(zhí)行時,如果有一個異常阻礙了Servlet的正常操作,那么這個異常就是根原因(root cause)異常。如果需要在一個Servlet異常中包含根原因的異常,可以調(diào)用這個構(gòu)造方法,同時包含一個描述消息。例如:可以在ServletException異常中嵌入一個java.sql.SQLException異常。

Ø  public ServletException(java.lang.Throwable rootCause)

該方法同上,只是沒有指定描述消息的參數(shù)。

Ø  public java.lang.Throwable getRootCause()

該方法返回引起這個Servlet異常的異常,也就是返回根原因的異常。

2.3.2  UnavailableException類

UnavailableException類是ServletException類的子類,該異常被Servlet拋出,用于向Servlet容器指示這個Servlet永久地或者暫時地不可用。這個類提供了下面2個構(gòu)造方法和2個實例方法:

Ø  public UnavailableException(java.lang.String msg)

該方法用一個給定的消息構(gòu)造一個新的異常,指示Servlet永久不可用。

Ø  public UnavailableException(java.lang.String msg, int seconds)

該方法用一個給定的消息構(gòu)造一個新的異常,指示Servlet暫時不可用。其中的參數(shù)seconds指明在這個以秒為單位的時間內(nèi),Servlet不可用。如果Servlet不能估計出多長時間后它將恢復(fù)功能,可以傳遞一個負數(shù)或零給seconds參數(shù)。

Ø  public int getUnavailableSeconds()

該方法返回Servlet預(yù)期的暫時不可用的秒數(shù)。如果返回一個負數(shù),表明Servlet永久不可用或者不能估計出Servlet多長時間不可用。

Ø  public boolean isPermanent()

該方法返回一個布爾值,用于指示Servlet是否是永久不可用。返回true,表明Servlet永久不可用;返回false,表明Servlet可用或者暫時不可用。

2.4   Servlet生命周期

Servlet運行在Servlet容器中,其生命周期由容器來管理。Servlet的生命周期通過javax.servlet.Servlet接口中的init()、service()和destroy()方法來表示。

Servlet的生命周期包含了下面4個階段:

(1)加載和實例化

Servlet容器負責加載和實例化Servlet。當Servlet容器啟動時,或者在容器檢測到需要這個Servlet來響應(yīng)第一個請求時,創(chuàng)建Servlet實例。當Servlet容器啟動后,它必須要知道所需的Servlet類在什么位置,Servlet容器可以從本地文件系統(tǒng)、遠程文件系統(tǒng)或者其他的網(wǎng)絡(luò)服務(wù)中通過類加載器加載Servlet類,成功加載后,容器創(chuàng)建Servlet的實例。因為容器是通過Java的反射API來創(chuàng)建Servlet實例,調(diào)用的是Servlet的默認構(gòu)造方法(即不帶參數(shù)的構(gòu)造方法),所以我們在編寫Servlet類的時候,不應(yīng)該提供帶參數(shù)的構(gòu)造方法。

(2)初始化

在Servlet實例化之后,容器將調(diào)用Servlet的init()方法初始化這個對象。初始化的目的是為了讓Servlet對象在處理客戶端請求前完成一些初始化的工作,如建立數(shù)據(jù)庫的連接,獲取配置信息等。對于每一個Servlet實例,init()方法只被調(diào)用一次。在初始化期間,Servlet實例可以使用容器為它準備的ServletConfig對象從Web應(yīng)用程序的配置信息(在web.xml中配置)中獲取初始化的參數(shù)信息。在初始化期間,如果發(fā)生錯誤,Servlet實例可以拋出ServletException異?;蛘遀navailableException異常來通知容器。ServletException異常用于指明一般的初始化失敗,例如沒有找到初始化參數(shù);而UnavailableException異常用于通知容器該Servlet實例不可用。例如,數(shù)據(jù)庫服務(wù)器沒有啟動,數(shù)據(jù)庫連接無法建立,Servlet就可以拋出UnavailableException異常向容器指出它暫時或永久不可用。

(3)請求處理

Servlet容器調(diào)用Servlet的service()方法對請求進行處理。要注意的是,在service()方法調(diào)用之前,init()方法必須成功執(zhí)行。在service()方法中,Servlet實例通過ServletRequest對象得到客戶端的相關(guān)信息和請求信息,在對請求進行處理后,調(diào)用ServletResponse對象的方法設(shè)置響應(yīng)信息。在service()方法執(zhí)行期間,如果發(fā)生錯誤,Servlet實例可以拋出ServletException異?;蛘遀navailableException異常。如果UnavailableException異常指示了該實例永久不可用,Servlet容器將調(diào)用實例的destroy()方法,釋放該實例。此后對該實例的任何請求,都將收到容器發(fā)送的HTTP 404(請求的資源不可用)響應(yīng)。如果UnavailableException異常指示了該實例暫時不可用,那么在暫時不可用的時間段內(nèi),對該實例的任何請求,都將收到容器發(fā)送的HTTP 503(服務(wù)器暫時忙,不能處理請求)響應(yīng)。

(4)服務(wù)終止

當容器檢測到一個Servlet實例應(yīng)該從服務(wù)中被移除的時候,容器就會調(diào)用實例的destroy()方法,以便讓該實例可以釋放它所使用的資源,保存數(shù)據(jù)到持久存儲設(shè)備中。當需要釋放內(nèi)存或者容器關(guān)閉時,容器就會調(diào)用Servlet實例的destroy()方法。在destroy()方法調(diào)用之后,容器會釋放這個Servlet實例,該實例隨后會被Java的垃圾收集器所回收。如果再次需要這個Servlet處理請求,Servlet容器會創(chuàng)建一個新的Servlet實例。

在整個Servlet的生命周期過程中,創(chuàng)建Servlet實例、調(diào)用實例的init()和destroy()方法都只進行一次,當初始化完成后,Servlet容器會將該實例保存在內(nèi)存中,通過調(diào)用它的service()方法,為接收到的請求服務(wù)。下面給出Servlet整個生命周期過程的UML序列圖,如圖2-16所示。

圖2-16  Servlet在生命周期內(nèi)為請求服務(wù)

如果需要讓Servlet容器在啟動時即加載Servlet,可以在web.xml文件中配置<load-on-startup>元素。具體配置方法,參見第3.1節(jié)。

2.5  Servlet上下文

運行在Java虛擬機中的每一個Web應(yīng)用程序都有一個與之相關(guān)的Servlet上下文。Java Servlet API提供了一個ServletContext接口用來表示上下文。在這個接口中定義了一組方法,Servlet可以使用這些方法與它的Servlet容器進行通信,例如,得到文件的MIME類型,轉(zhuǎn)發(fā)請求,或者向日志文件中寫入日志消息。

ServletContext對象是Web服務(wù)器中的一個已知路徑的根。對于本章的實例,Servlet上下文被定位于http://localhost:8080/ch02。以/ch02請求路徑(稱為上下文路徑)開始的所有請求被發(fā)送到與此ServletContext關(guān)聯(lián)的Web應(yīng)用程序。

Servlet容器提供商負責提供ServletContext接口的實現(xiàn)。Servlet容器在Web應(yīng)用程序加載時創(chuàng)建ServletContext對象,作為Web應(yīng)用程序的運行時表示,ServletContext對象可以被Web應(yīng)用程序中所有的Servlet所訪問。

2.5.1  ServletContext接口

一個ServletContext對象表示了一個Web應(yīng)用程序的上下文。Servlet容器在Servlet初始化期間,向其傳遞ServletConfig對象,可以通過ServletConfig對象的getServletContext()方法來得到ServletContext對象。也可以通過GenericServlet類的getServletContext()方法得到ServletContext對象,不過GenericServlet類的getServletContext()也是調(diào)用ServletConfig對象的getServletContext()方法來得到這個對象的。

ServletContext接口定義了下面的這些方法,Servlet容器提供了這個接口的實現(xiàn)。

Ø  public java.lang.Object getAttribute(java.lang.String name)

Ø  public java.util.Enumeration getAttributeNames()

Ø  public void removeAttribute(java.lang.String name)

Ø  public void setAttribute(java.lang.String name, java.lang.Object object)

上面4個方法用于讀取、移除和設(shè)置共享屬性,任何一個Servlet都可以設(shè)置某個屬性,而同一個Web應(yīng)用程序的另一個Servlet可以讀取這個屬性,不管這些Servlet是否為同一個客戶進行服務(wù)。

Ø  public ServletContext getContext(java.lang.String uripath)

該方法返回服務(wù)器上與指定的URL相對應(yīng)的ServletContext對象。給出的uripath參數(shù)必須以斜杠(/)開始,被解釋為相對于服務(wù)器文檔根的路徑。出于安全方面的考慮,如果調(diào)用該方法訪問一個受限制的ServletContext對象,那么該方法將返回null。

Ø  public String getContextPath()

該方法是在Servlet 2.5規(guī)范中新增的,用于返回Web應(yīng)用程序的上下文路徑。上下文路徑總是以斜杠(/)開頭,但結(jié)束沒有斜杠(/)。在默認(根)上下文中,這個方法返回空字符串""。

Ø  public java.lang.String getInitParameter(java.lang.String name)
Ø  public java.util.Enumeration getInitParameterNames()

可以為Servlet上下文定義初始化參數(shù),這些參數(shù)被整個Web應(yīng)用程序所使用??梢栽诓渴鹈枋龇╳eb.xml)中使用<context-param>元素來定義上下文的初始化參數(shù),上面兩個方法用于訪問這些參數(shù)。

Ø  public int getMajorVersion()
Ø  public int getMinorVersion()

上面兩個方法用于返回Servlet容器支持的Java Servlet API的主版本和次版本號。例如,對于遵從Servlet 2.4版本的容器,getMajorVersion()方法返回2,getMinorVersion()方法返回4。

Ø  public java.lang.String getMimeType(java.lang.String file)

該方法返回指定文件的MIME類型,如果類型是未知的,這個方法將返回null。MIME類型的檢測是根據(jù)Servlet容器的配置,也可以在Web應(yīng)用程序的部署描述符中指定。

Ø  public RequestDispatcher getRequestDispatcher(java.lang.String path)

該方法返回一個RequestDispatcher對象,作為指定路徑上的資源的封裝??梢允褂肦equestDispatcher對象將一個請求轉(zhuǎn)發(fā)(forward)給其他資源進行處理,或者在響應(yīng)中包含(include)資源。要注意的是,傳入的參數(shù)path必須以斜杠(/)開始,被解釋為相對于當前上下文根(context root)的路徑。

Ø  public RequestDispatcher getNamedDispatcher(java.lang.String name)

該方法與getRequestDispatcher()方法類似。不同之處在于,該方法接受一個在部署描述符中以<servlet-name>元素給出的Servlet(或JSP頁面)的名字作為參數(shù)。

Ø  public java.lang.String getRealPath(java.lang.String path)

在一個Web應(yīng)用程序中,資源用相對于上下文路徑的路徑來引用,這個方法可以返回資源在服務(wù)器文件系統(tǒng)上的真實路徑(文件的絕對路徑)。返回的真實路徑的格式應(yīng)該適合于運行這個Servlet容器的計算機和操作系統(tǒng)(包括正確的路徑分隔符)。如果Servlet容器不能夠?qū)⑻摂M路徑轉(zhuǎn)換為真實的路徑,這個方法將會返回null。

Ø  public java.net.URL getResource(java.lang.String path) throws java.net.Malformed URLException

該方法返回被映射到指定路徑上的資源的URL。傳入的參數(shù)path必須以斜杠(/)開始,被解釋為相對于當前上下文根(context root)的路徑。這個方法允許Servlet容器從任何來源為Servlet生成一個可用的資源。資源可以是在本地或遠程文件系統(tǒng)上,在數(shù)據(jù)庫中,或者在WAR文件中。如果沒有資源映射到指定的路徑上,該方法將返回null。

Ø  public java.io.InputStream getResourceAsStream(java.lang.String path)

該方法與getResource()方法類似,不同之處在于,該方法返回資源的輸入流對象。另外,使用getResourceAsStream()方法,元信息(如內(nèi)容長度和內(nèi)容類型)將丟失,而使用getResource()方法,元信息是可用的。

Ø  public java.util.Set getResourcePaths(java.lang.String path)

該方法返回資源的路徑列表,參數(shù)path必須以斜杠(/)開始,指定用于匹配資源的部分路徑。例如,一個Web應(yīng)用程序包含了下列資源:

—  /welcome.html

—  /catalog/index.html

—  /catalog/products.html

—  /catalog/offers/books.html

—  /catalog/offers/music.html

—  /customer/login.jsp

—  /WEB-INF/web.xml

—  /WEB-INF/classes/com.acme.OrderServlet.class

如果調(diào)用getResourcePaths("/"),將返回[/welcome.html, /catalog/, /customer/, /WEB-INF/]。如果調(diào)用getResourcePaths("/catalog/"),將返回[/catalog/index.html, /catalog/products.html, /catalog/offers/]。

Ø  public java.lang.String getServerInfo()

該方法返回運行Servlet的容器的名稱和版本。

Ø  public java.lang.String getServletContextName()

該方法返回在部署描述符中使用<display-name>元素指定的對應(yīng)于當前ServletContext的Web應(yīng)用程序的名稱。

Ø  public void log(java.lang.String msg)
Ø  public void log(java.lang.String message, java.lang.Throwable throwable)

ServletContext接口提供了上面兩個記錄日志的方法,第一個方法用于記錄一般的日志,第二個方法用于記錄指定異常的棧跟蹤信息。

2.5.2  頁面訪問量統(tǒng)計實例

有時候,我們可能需要統(tǒng)計Web站點上的一個特定頁面的訪問次數(shù),考慮這樣一個場景,你為了宣傳一個產(chǎn)品,在某個門戶網(wǎng)站花錢做了一個鏈接,你希望知道產(chǎn)品頁面每天的訪問量,借此了解廣告的效果。要完成上述功能,可以使用ServletContext對象來保存訪問的次數(shù)。我們知道一個Web應(yīng)用程序只有一個ServletContext對象,而且該對象可以被Web應(yīng)用程序中的所有Servlet所訪問,因此使用ServletContext對象來保存一些需要在Web應(yīng)用程序中共享的信息是再合適不過了。

要在ServletContext對象中保存共享信息,可以調(diào)用該對象的setAttribute()方法,要獲取共享信息,可以調(diào)用該對象的getAttribute()方法。針對本例,我們可以調(diào)用setAttribute()方法將訪問計數(shù)保存到上下文對象中,新增一次訪問時,調(diào)用getAttribute()方法從上下文對象中取出訪問計數(shù)加1,然后再調(diào)用setAttribute()方法保存回上下文對象中。這個實例的開發(fā)主要有下列步驟。

Step1:編寫CounterServlet類

在%CATALINA_HOME%\webapps\ch02\src目錄下新建CounterServlet.java,代碼如例2-14所示。

例2-14  CounterServlet.java

1.  package org.sunxin.ch02.servlet;

2. 

3.  import java.io.IOException;

4.  import java.io.PrintWriter;

5. 

6.  import javax.servlet.ServletContext;

7.  import javax.servlet.ServletException;

8.  import javax.servlet.http.HttpServlet;

9.  import javax.servlet.http.HttpServletRequest;

10. import javax.servlet.http.HttpServletResponse;

11.

12. public class CounterServlet extends HttpServlet

13. {

14.     public void doGet(HttpServletRequest req, HttpServletResponse resp)

15.             throws ServletException, IOException

16.     {

17.         ServletContext context = getServletContext();

18.         Integer count = null;

19.         synchronized(context)

20.         {

21.             count = (Integer) context.getAttribute("counter");

22.             if (null == count)

23.             {

24.                 count = new Integer(1);

25.             }

26.             else

27.             {

28.                 count = new Integer(count.intValue() + 1);

29.             }

30.            context.setAttribute("counter", count);

31.         }

32.        

33.         resp.setContentType("text/html;charset=gb2312");

34.         PrintWriter out = resp.getWriter();

35.

36.         out.println("<html><head>");

37.         out.println("<title>頁面訪問統(tǒng)計</title>");

38.         out.println("</head><body>");

39.         out.println("該頁面已被訪問了" + "<b>" + count + "</b>" + "次");

40.         out.println("</body></html>");

41.         out.close();

42.     }

43. }

在程序代碼的第17行,調(diào)用getServletContext()方法(從GenericServlet類間接繼承而來)得到Web應(yīng)用程序的上下文對象。為了避免線程安全的問題,我們在第19行使用synchronized關(guān)鍵字對context對象進行同步。第21行,調(diào)用上下文對象的getAttribute()方法獲取counter屬性的值。第21~29行,判斷count是否為null,如果為null,則將它的初始值設(shè)為1。當這個Servlet第一次被訪問的時候,在上下文對象中還沒有保存counter屬性,所以獲取該屬性的值將返回null。如果count不為null,則將count加1。第30行,將count作為counter屬性的值保存到ServletContext對象中。當下一次訪問這個Servlet時,調(diào)用getAttribute()方法取出counter屬性的值不為null,于是執(zhí)行第28行的代碼,將count加1,此時count為2,表明頁面被訪問了兩次。

第39行,輸出count,顯示該頁面的訪問次數(shù)。

Step2:編譯CounterServlet.java

打開命令提示符,進入%CATALINA_HOME%\webapps\ch02\src目錄,然后執(zhí)行:

javac -d ..\WEB-INF\classes CounterServlet.java

在WEB-INF\classes\org\sunxin\ch02\servlet目錄中生成類文件CounterServlet.class。

Step3:部署CounterServlet

編輯WEB-INF目錄下的web.xml文件,添加對本例中的Servlet的配置,完整的內(nèi)容如例2-15所示。

例2-15  web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5"

    xmlns="http://java.sun.com/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

 

    <servlet>

        <servlet-name>HelloWorldServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.HelloWorldServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>HelloWorldServlet</servlet-name>

        <url-pattern>/helloworld</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>WelcomeServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.WelcomeServlet </servlet-class>

        <init-param>

            <param-name>greeting</param-name>

            <param-value>Welcome you</param-value>

        </init-param>

    </servlet>

   

    <servlet-mapping>

        <servlet-name>WelcomeServlet</servlet-name>

        <url-pattern>/welcome</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>OutputInfoServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.OutputInfoServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>OutputInfoServlet</servlet-name>

        <url-pattern>/info</url-pattern>

    </servlet-mapping>

   

    <servlet>

        <servlet-name>LoginServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.LoginServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>LoginServlet</servlet-name>

        <url-pattern>/login</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>CounterServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.CounterServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>CounterServlet</servlet-name>

        <url-pattern>/product.html</url-pattern>

    </servlet-mapping>

</web-app>

新增加的內(nèi)容以粗體顯示,請讀者注意,在Servlet映射中,我們?yōu)楸纠腟ervlet指定的URL是/product.html,對用戶來說,以為訪問的是一個靜態(tài)頁面,利用部署描述符,可以向客戶端屏蔽服務(wù)器端的實現(xiàn)細節(jié)。

Step4:訪問CounterServlet

啟動Tomcat服務(wù)器,打開IE瀏覽器,在地址欄中輸入http://localhost:8080/ch02/product.html,你將看到如圖2-17所示的頁面。

單擊圖2-17中的刷新按鈕,你會看到訪問的次數(shù)變?yōu)?。再打開一個瀏覽器,輸入:http://localhost: 8080/ch02/product.html,你會看到第二個瀏覽器中顯示的訪問次數(shù)是3。交替刷新兩個瀏覽器中的頁面,可以看到訪問次數(shù)也在交替增長,說明利用ServletContext保存屬性,可以在多個客戶端之間共享屬性。但要注意的是,不同的Web應(yīng)用程序具有不同的Servlet上下文,所以在不同的Web應(yīng)用程序之間不能利用ServletContext來共享屬性。另外還需要注意的是,訪問次數(shù)在重啟Tomcat服務(wù)器后,將重新從1開始,為了永久保存訪問次數(shù),可以將這個值保存到文件或數(shù)據(jù)庫中。

2.6   請求轉(zhuǎn)發(fā)

考慮生活中的一個場景,110報警中心收到群眾報警電話,根據(jù)報警的內(nèi)容(報警地點、事情緊急程度),將報警請求交由不同的派出所進行處理。在這里,110報警中心充當了一個調(diào)度員的角色,它負責將各種報警請求轉(zhuǎn)發(fā)給實際的處理單位。這種處理模型的好處是:

① 給人們提供了統(tǒng)一的報警方式(撥打110)。

② 另一方面,報警中心可以根據(jù)報案人所處的位置、派出所的地理位置與人員狀況,合理調(diào)度資源,安排就近的派出所及時出警。

③ 報警中心并不處理具體的案件,縮短了對報警請求的響應(yīng)時間。

在Web應(yīng)用中,這種處理模型也得到了廣泛的應(yīng)用(參見第10.2節(jié)),這種調(diào)度員的角色通常由Servlet來充當,我們把這樣的Servlet叫做控制器(Controller)。在控制器中,可以將請求轉(zhuǎn)發(fā)(request dispatching)給另外一個Servlet或者JSP頁面,甚至是靜態(tài)的HTML頁面,然后由它們進行處理并產(chǎn)生對請求的響應(yīng)。要完成請求轉(zhuǎn)發(fā),就要用到j(luò)avax.servlet.RequestDispatcher接口。

2.6.1  RequestDispatcher接口

RequestDispatcher對象由Servlet容器創(chuàng)建,用于封裝一個由路徑所標識的服務(wù)器資源。利用RequestDispatcher對象,可以把請求轉(zhuǎn)發(fā)給其他的Servlet或JSP頁面。在RequestDispatcher接口中定義了兩種方法。

Ø  public void forward(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException

該方法用于將請求從一個Servlet傳遞給服務(wù)器上的另外的Servlet、JSP頁面或者是HTML文件。在Servlet中,可以對請求做一個初步的處理,然后調(diào)用這個方法,將請求傳遞給其他的資源來輸出響應(yīng)。要注意的是,這個方法必須在響應(yīng)被提交給客戶端之前調(diào)用,否則的話,它將拋出IllegalStateException異常。在forward()方法調(diào)用之后,原先在響應(yīng)緩存中的沒有提交的內(nèi)容將被自動清除。

Ø  public void include(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException

該方法用于在響應(yīng)中包含其他資源(Servlet、JSP頁面或HTML文件)的內(nèi)容。和forward()方法的區(qū)別在于:利用include()方法將請求轉(zhuǎn)發(fā)給其他的Servlet,被調(diào)用的Servlet對該請求做出的響應(yīng)將并入原先的響應(yīng)對象中,原先的Servlet還可以繼續(xù)輸出響應(yīng)信息。而利用forward()方法將請求轉(zhuǎn)發(fā)給其他的Servlet,將由被調(diào)用的Servlet負責對請求做出響應(yīng),而原先Servlet的執(zhí)行則終止。

2.6.2  得到RequestDispatcher對象

有三種方法可以用來得到RequestDispatcher對象。一是利用ServletRequest接口中的getRequestDispatcher()方法:

Ø  public RequestDispatcher getRequestDispatcher(java.lang.String path)

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

Ø  public RequestDispatcher getRequestDispatcher(java.lang.String path)
Ø  public RequestDispatcher getNamedDispatcher(java.lang.String name)

可以看到ServletRequest接口和ServletContext接口各自提供了一個同名的方法getRequestDispatcher(),那么這兩個方法有什么區(qū)別呢?兩個getRequestDispatcher()方法的參數(shù)都是資源的路徑名,不過ServletContext接口中的getRequestDispatcher()方法的參數(shù)必須以斜杠(/)開始,被解釋為相對于當前上下文根(context root)的路徑。例如:/myservlet是合法的路徑,而../myservlet是不合法的路徑;而ServletRequest接口中的getRequestDispatcher()方法的參數(shù)不但可以是相對于上下文根的路徑,而且可以是相對于當前Servlet的路徑。例如:/myservlet和myservlet都是合法的路徑,如果路徑以斜杠(/)開始,則被解釋為相對于當前上下文根的路徑;如果路徑?jīng)]有以斜杠(/)開始,則被解釋為相對于當前Servlet的路徑。ServletContext接口中的getNamedDispatcher()方法則是以在部署描述符中給出的Servlet(或JSP頁面)的名字作為參數(shù)。

調(diào)用ServletContext對象的getContext()方法可以獲取另一個Web應(yīng)用程序的上下文對象,利用該上下文對象調(diào)用getRequestDispatcher()方法得到的RequestDispatcher對象,可以將請求轉(zhuǎn)發(fā)到另一個Web應(yīng)用程序中的資源。但要注意的是,要跨Web應(yīng)用程序訪問資源,需要在當前Web應(yīng)用程序的<context>元素的設(shè)置中,指定crossContext屬性的值為true(參見第3章)。

2.6.3  請求轉(zhuǎn)發(fā)的實例

在這個例子中,我們編寫一個PortalServlet,在這個Servlet中,首先判斷訪問用戶是否已經(jīng)登錄,如果沒有登錄,則調(diào)用RequestDispatcher接口的include()方法,將請求轉(zhuǎn)發(fā)給LoginServlet2(為了和第2.2.3節(jié)的LoginServlet區(qū)分),LoginServlet2在響應(yīng)中發(fā)送登錄表單;如果已經(jīng)登錄,則調(diào)用RequestDispatcher接口的forward()方法,將請求轉(zhuǎn)發(fā)給第2.2.1節(jié)的WelcomeServlet,向用戶顯示歡迎信息。實例的開發(fā)主要有下列步驟。

Step1:編寫PortalServlet類

在%CATALINA_HOME%\webapps\ch02\src目錄下新建PortalServlet.java,代碼如例2-16所示。

例2-16  PortalServlet.java

1.  package org.sunxin.ch02.servlet;

2. 

3.  import java.io.IOException;

4.  import java.io.PrintWriter;

5. 

6.  import javax.servlet.RequestDispatcher;

7.  import javax.servlet.ServletContext;

8.  import javax.servlet.ServletException;

9.  import javax.servlet.http.HttpServlet;

10. import javax.servlet.http.HttpServletRequest;

11. import javax.servlet.http.HttpServletResponse;

12.

13. public class PortalServlet extends HttpServlet

14. {

15.     public void doGet(HttpServletRequest req, HttpServletResponse resp)

16.                throws ServletException,IOException

17.     {

18.         resp.setContentType("text/html;charset=gb2312");

19.        

20.         PrintWriter out=resp.getWriter();

21.        

22.         out.println("<html><head><title>");

23.         out.println("登錄頁面");

24.         out.println("</title></head><body>");

25.        

26.         String name=req.getParameter("username");

27.         String pwd=req.getParameter("password");

28.        

29.         if("zhangsan".equals(name) && "1234".equals(pwd))

30.         {

31.             ServletContext context=getServletContext();

32.             RequestDispatcher rd=context.getRequestDispatcher("/welcome");

33.             rd.forward(req,resp);

34.         }

35.         else

36.         {

37.             RequestDispatcher rd=req.getRequestDispatcher("login2");

38.             rd.include(req,resp);

39.         }

40.         out.println("</body></html>");

41.         out.close();

42.     }

43.     public void doPost(HttpServletRequest req,HttpServletResponse resp)

44.                 throws ServletException,IOException

45.     {

46.         doGet(req,resp);

47.     }

48. }

為了比較RequestDispatcher的forward()和include()方法的區(qū)別,在doGet()方法的開始和結(jié)尾處分別輸出了一段HTML代碼。第22~24行輸出一段HTML代碼,這段HTML代碼和第40行輸出的HTML代碼組成了完整的HTML文檔。第26~27行,從請求中獲取用戶名和密碼。

第29行是對用戶名和密碼進行判斷的代碼,在這里,我們使用了一個小技巧,即直接用"zhangsan"和"1234"這種字面量字符串調(diào)用equals()方法來判斷用戶名和密碼,這樣,即使name和pwd為null,程序也不會出錯,判斷仍然正常執(zhí)行。在第2.2.3節(jié)的LoginServlet中,為了避免出現(xiàn)空指針異常,我們要先判斷name和pwd是否為null,而采用本例的這種方式,就不需要進行這樣的判斷了。

第31~33行,如果用戶名和密碼正確,則利用上下文對象的getRequestDispatcher()方法得到RequestDispatcher對象,傳入的路徑參數(shù)必須以斜杠(/)開始,然后利用forward()方法將請求轉(zhuǎn)發(fā)給welcome這個Servlet處理。請讀者注意,在forward()方法調(diào)用之后,我們在第22~24行輸出的HTML代碼將自動被清除,執(zhí)行的控制權(quán)將交給welcome,在doGet()方法中剩余的代碼也不再執(zhí)行。第37~38行,如果用戶沒有登錄或者輸入了錯誤的用戶名或密碼,則利用請求對象的getRequestDispatcher()方法得到RequestDispatcher對象,傳入的路徑參數(shù)沒有以斜杠(/)開始,表示相對于當前Servlet的路徑,然后調(diào)用include()方法將請求轉(zhuǎn)發(fā)給login2這個Servlet處理,當login2對請求處理完畢后,執(zhí)行的控制權(quán)回到PortalServlet,將繼續(xù)執(zhí)行第40~41行的代碼。

Step2:編寫LoginServlet2類

在%CATALINA_HOME%\webapps\ch02\src目錄下新建LoginServlet2.java,代碼如例2-17所示。

例2-17  LoginServlet2.java

1.  package org.sunxin.ch02.servlet;

2. 

3.  import java.io.IOException;

4.  import java.io.PrintWriter;

5. 

6.  import javax.servlet.ServletException;

7.  import javax.servlet.http.HttpServlet;

8.  import javax.servlet.http.HttpServletRequest;

9.  import javax.servlet.http.HttpServletResponse;

10.

11. public class LoginServlet2 extends HttpServlet

12. {

13.     public void doGet(HttpServletRequest req, HttpServletResponse resp)

14.                throws ServletException,IOException

15.     {

16.        

17.         resp.setContentType("text/html;charset=gb2312");

18.        

19.         PrintWriter out=resp.getWriter();

20.

21.         out.println("<form method=post action=portal>");

22.        

23.         out.println("<table>");

24.        

25.         out.println("<tr>");

26.         out.println("<td>請輸入用戶名</td>");

27.         out.println("<td><input type=text name=username></td>");

28.         out.println("</tr>");

29.        

30.         out.println("<tr>");

31.         out.println("<td>請輸入密碼</td>");

32.         out.println("<td><input type=password name=password></td>");

33.         out.println("</tr>");

34.        

35.         out.println("<tr>");

36.         out.println("<td><input type=reset value=重填></td>");

37.         out.println("<td><input type=submit value=登錄></td>");

38.         out.println("</tr>");

39.        

40.         out.println("</table>");

41.         out.println("</form>");

42.     }

43.    

44.     public void doPost(HttpServletRequest req,HttpServletResponse resp)

45.                throws ServletException,IOException

46.     {

47.         doGet(req,resp);

48.     }

49. }

代碼的第21~41行,主要是輸出一個登錄的表單,因為在例2-16的PortalServlet中,已經(jīng)輸出了<html>、<head>、<title>和<body>元素,所以在這里就不需要再輸出這些元素了,這里輸出的表單將嵌入到<body>元素的開始標簽和結(jié)束標簽之間。要注意,在doGet()方法的最后,不要調(diào)用out.close()關(guān)閉輸出流對象,因為一旦關(guān)閉,響應(yīng)將被提交,那么在PortalServlet中調(diào)用include()方法之后的代碼將不再有效。

Step3:編譯PortalServlet.java和LoginServlet2.java

打開命令提示符,進入%CATALINA_HOME%\webapps\ch02\src目錄,然后執(zhí)行:

javac -d ..\WEB-INF\classes PortalServlet.java

javac -d ..\WEB-INF\classes LoginServlet2.java

在WEB-INF\classes\org\sunxin\ch02\servlet目錄中生成類文件PortalServlet.class和LoginServlet2.class。

Step4:部署PortalServlet和LoginServlet2

編輯WEB-INF目錄下的web.xml文件,添加對本例中的Servlet的配置,完整的內(nèi)容如例2-18所示。

例2-18  web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5"

    xmlns="http://java.sun.com/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

 

    <servlet>

        <servlet-name>HelloWorldServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.HelloWorldServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>HelloWorldServlet</servlet-name>

        <url-pattern>/helloworld</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>WelcomeServlet</servlet-name>

        <servlet-class>

            org.sunxin.ch02.servlet.WelcomeServlet </servlet-class>

 

        <init-param>

            <param-name>greeting</param-name>

            <param-value>Welcome you</param-value>

        </init-param>

    </servlet>

   

    <servlet-mapping>

        <servlet-name>WelcomeServlet</servlet-name>

        <url-pattern>/welcome</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>OutputInfoServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.OutputInfoServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>OutputInfoServlet</servlet-name>

        <url-pattern>/info</url-pattern>

    </servlet-mapping>

   

    <servlet>

        <servlet-name>LoginServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.LoginServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>LoginServlet</servlet-name>

        <url-pattern>/login</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>CounterServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.CounterServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>CounterServlet</servlet-name>

        <url-pattern>/product.html</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>PortalServlet</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.PortalServlet </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>PortalServlet</servlet-name>

        <url-pattern>/portal</url-pattern>

    </servlet-mapping>

    

    <servlet>

        <servlet-name>LoginServlet2</servlet-name>

        <servlet-class>

             org.sunxin.ch02.servlet.LoginServlet2 </servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>LoginServlet2</servlet-name>

        <url-pattern>/login2</url-pattern>

    </servlet-mapping>

</web-app>

新增加的內(nèi)容以粗體顯示,在本例中用到的第2.2.1節(jié)的WelcomeServlet的配置內(nèi)容以斜體顯示。

Step5:訪問PortalServlet

啟動Tomcat服務(wù)器,打開IE瀏覽器,在地址欄中輸入http://localhost:8080/ch02/portal,你將看到如圖2-18所示的頁面。

因為第一次訪問時,用戶還沒有登錄,因此PortalServlet會先輸出一部分HTML代碼,然后利用RequestDispatcher對象的include()方法調(diào)用LoginServlet2 對請求進行響應(yīng),LoginServlet2輸出登錄表單后,PortalServlet繼續(xù)輸出剩余的HTML代碼,最后關(guān)閉輸出流對象,提交響應(yīng)。我們可以單擊瀏覽器菜單欄上的【查看】菜單,選擇【源文件】,或者在頁面上單擊鼠標右鍵,在彈出的快捷菜單中選擇【查看源文件】,就可以看到此次響應(yīng)輸出的HTML代碼。

在登錄表單中輸入用戶名zhangsan和密碼1234,單擊“登錄”提交按鈕,你將看到如圖2-19所示的頁面。

圖2-19  登錄成功后,PortalServlet經(jīng)由WelcomeServlet發(fā)回的歡迎信息

當用戶輸入了正確的用戶名和密碼,PortalServlet利用forward()方法將請求轉(zhuǎn)發(fā)給WelcomeServlet進行處理,而WelcomeServlet向用戶發(fā)回了歡迎信息。在PortalServlet中調(diào)用forward()方法之前輸出的沒有提交的HTML代碼被清除,而在調(diào)用forward()方法之后,執(zhí)行的控制權(quán)轉(zhuǎn)到WelcomeServlet,PortalServlet中剩余的代碼也不再有效。為了驗證PortalServlet輸出的HTML代碼確實被清除了,讀者可以查看歡迎頁面的源代碼,就可以證實這一點。

請讀者注意圖2-18和圖2-19中瀏覽器的地址欄部分,可以看到不管對請求進行處理的是哪一個Servlet,地址欄中始終顯示的是:http://localhost:8080/ch02/portal。

2.6.4  sendRedirect()和forward()方法的區(qū)別

HttpServletResponse接口的sendRedirect()方法和RequestDispatcher接口的forward()方法都可以利用另外的資源(Servlet、JSP頁面或HTLM文件)來為客戶端進行服務(wù),但是這兩種方法有著本質(zhì)上的區(qū)別。

下面分別給出了sendRedirectt()方法和forward()方法的工作原理圖,如圖2-20和圖2-21所示。

圖2-20  sendRedirect()方法的工作原理圖

圖2-20的交互過程如下:

① 瀏覽器訪問Servlet1。

② Servlet1想讓Servlet2為客戶端服務(wù)。

③ Servlet1調(diào)用sendRedirect()方法,將客戶端的請求重定向到Servlet2。

④ 瀏覽器訪問Servlet2。

⑤ Servlet2對客戶端的請求做出響應(yīng)。

從圖2-20中的交互過程可以看出,調(diào)用sendRedirect()方法,實際上是告訴瀏覽器Servlet2所在的位置,讓瀏覽器重新訪問Servlet2。調(diào)用sendRedirect()方法,會在響應(yīng)中設(shè)置Location響應(yīng)報頭。要注意的是,這個過程對于用戶來說是透明的,瀏覽器會自動完成新的訪問。從圖2-14瀏覽器的地址欄中,可以看到,顯示的URL是重定向之后的URL。

②forward()

 

圖2-21  forward()方法的工作原理圖

圖2-21的交互過程如下:

① 瀏覽器訪問Servlet1。

② Servlet1想讓Servlet2對客戶端的請求進行響應(yīng),于是調(diào)用forward()方法,將請求轉(zhuǎn)發(fā)給Servlet2進行處理。

③ Servlet2對請求做出響應(yīng)。

從圖2-21中的交互過程可以看出,調(diào)用forward()方法,對瀏覽器來說是透明的,瀏覽器并不知道為其服務(wù)的Servlet已經(jīng)換成Servlet2了,它只知道發(fā)出了一個請求,獲得了一個響應(yīng)。從圖2-18和圖2-19瀏覽器的地址欄中,可以看到,顯示的URL始終是原始請求的URL。

sendRedirect()方法和forward()方法還有一個區(qū)別,那就是sendRedirect()方法不但可以在位于同一主機上的不同Web應(yīng)用程序之間進行重定向,而且可以將客戶端重定向到其他服務(wù)器上的Web應(yīng)用程序資源。

2.7   小結(jié)

本章內(nèi)容較多,首先介紹了Servlet API中的主要接口及其實現(xiàn)類,包括了與Servlet實現(xiàn)相關(guān)的Servlet接口、GenericServlet抽象類和HttpServlet抽象類,與請求和響應(yīng)相關(guān)的ServletRequest接口、ServletResponse接口、HttpServletRequest接口和HttpServletResponse接口,與Servlet配置相關(guān)的ServletConfig接口,并通過4個實例幫助讀者更好地理解這些接口與類的作用與用法。

其次,我們介紹了Servlet中的異常類:ServletException類和UnavailableException類,UnavailableException類是ServletException類的子類,在后面的章節(jié)中,我們會通過具體的例子來演示這些異常類的用法。

再次,介紹了Servlet生命周期,Servlet的生命周期包括了加載和實例化、初始化、請求處理、服務(wù)終止4個階段,在編寫Servlet時,要根據(jù)Servlet的生命周期,來安排程序功能的實現(xiàn)位置,例如,資源的分配與初始化可以放到init()方法中,主要的請求處理代碼可以放到相應(yīng)的doXXX()方法中,資源的釋放可以放到destroy()方法中。

然后,我們介紹了Servlet上下文,每一個Web應(yīng)用程序都有一個與之相關(guān)的Servlet上下文。Java Servlet API提供了一個ServletContext接口用來表示上下文,ServletContext對象是Web應(yīng)用程序的運行時表示,ServletContext對象可以被Web應(yīng)用程序中所有的Servlet所訪問,利用ServletContext對象可以在多個客戶端之間、多個Servlet之間(要求在同一個Web應(yīng)用程序下)共享屬性。

最后,我們介紹了請求轉(zhuǎn)發(fā)。利用RequestDispatcher對象,可以把請求轉(zhuǎn)發(fā)給其他的Servlet或JSP頁面。讀者要了解RequestDispatcher接口的forward()方法和include()方法的區(qū)別,掌握HttpServletResponse對象的sendRedirect()方法和RequestDispatcher對象的forward()方法的工作原理。在實際應(yīng)用中,可以根據(jù)具體的情況,合理利用這兩種方法來完成業(yè)務(wù)需求。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
在Servlet使用getServletContext()獲取ServletContext對象出現(xiàn)java.lang.NullPointerException(空指針)異常的解決辦法
Servlet技術(shù)(上)
Servlet API——ServletContext接口的簡述
四大域總結(jié)
關(guān)于serveletContext.getRealPath()方法
Jsp技術(shù)總結(jié)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服