官术网_书友最值得收藏!

  • Java Web程序設計
  • 張磊 丁香乾編著
  • 1167字
  • 2018-12-29 18:50:49

第2章 Servlet會話跟蹤

本章目標

■ 掌握會話跟蹤的相關技術

■ 理解Cookie的原理

■ 掌握Cookie的讀寫方法使用

■ 理解Session的原理

■ 理解Session的生命周期

■ 熟練掌握Session的方法使用

■ 掌握ServletContext的方法使用

學習導航

任務描述

【描述2.D.1】

使用Cookie將用戶的登錄信息保存到客戶端,當用戶再次登錄時在相應的文本欄中顯示上次登錄時輸入的信息。

【描述2.D.2】

用戶通過表單提交數據,使用Session在該用戶訪問的所有頁面中輸出此數據。

【描述2.D.3】

基于任務描述2.D.2,通過URL重寫來實現Session會話技術。

【描述2.D.4】

使用ServletContext顯示在線訪問人數。

【描述2.D.5】

編寫一個Servlet,訪問web.xml中的初始化參數信息。

2.1 會話跟蹤簡介

HTTP是一種無狀態的協議,這就意味著Web服務器并不了解同一用戶以前請求的信息,即當瀏覽器與服務器之間的請求、響應結束后,服務器上不會保留任何客戶端的信息。但對于現在的Web應用而言,往往需要記錄特定客戶端的一系列請求之間的聯系,以便于對客戶的狀態進行追蹤。比如,在購物網站,服務器會為每個客戶配置一個購物車,購物車需要一直跟隨客戶,以便于客戶將商品放入購物車中,而且每個客戶之間的購物車也不會混淆。這就是本章所講到的會話跟蹤技術。

會話跟蹤技術的方案包括以下幾種:

■ Cookie技術

■ Session技術

■ URL重寫技術

■ 隱藏表單域技術

注意 由于隱藏表單域技術是將會話ID添加到隱藏域中,實現起來較為煩瑣,因此在實際應用中不推薦使用該技術,本章也不做講解。

2.2 Cookie

Cookie是服務器發給客戶端的一小段文本,保存在瀏覽器所在客戶端的內存或磁盤上。服務器可以從客戶端讀出這些Cookie。通過Cookie,客戶端和服務器端可建立起一種聯系,也就是說,Cookie是一種可以讓服務器對客戶端信息進行保存和獲取的機制,從而大大擴展了基于Web的應用功能。

Cookie是會話跟蹤的一種解決方案,例如在需要登錄的網站,用戶第一次輸入用戶名和密碼后,可以將其利用Cookie保存在客戶端,當用戶下一次訪問這個網站的時候,就能直接從客戶端讀出該用戶名和密碼來,用戶就不需要每次都重新登錄。另外,也可以根據需要讓用戶定制自己喜歡的內容,用戶可以選擇自己喜歡的新聞、顯示的風格、顯示的順序等,這些相關的設置信息都保存到客戶端的Cookie中,當用戶每次訪問該網站時,就可以按照他預設的內容進行顯示。

當然,因為Cookie需要將信息保存在客戶端的計算機上,所以,從Cookie誕生之日起,有關于它所可能帶來的安全問題就一直是人們所關注的焦點。但截至目前,還沒有因為Cookie所帶來的重大安全問題,這主要也是由Cookie的安全機制所決定的:

■ Cookie不會以任何方式在客戶端被執行。

■ 瀏覽器會限制來自同一個網站的Cookie數目。

■ 單個Cookie的長度是有限制的。

■ 瀏覽器限制了最多可以接受的Cookie數目。

基于這些安全機制,客戶端就不必擔心硬盤被這些Cookie占用太大的空間。雖然Cookie不太可能帶來安全問題,但可能會帶來一些隱私問題,通常情況下,不要將敏感的信息保存到Cookie中,特別是一些重要的個人資料,如信用卡賬號、密碼等。另外,瀏覽器可以設置成拒絕Cookie,因此Web開發中不要使程序過度依賴于Cookie,因為一旦用戶關閉了瀏覽器的Cookie功能,就可能造成程序無法正確運行。

2.2.1 Cookie的創建及使用

通過Cookie類的構造方法可以創建該類的實例。Cookie的構造方法帶有兩個String類型的參數,分別用于指定Cookie的屬性名稱和屬性值,例如:

    Cookie userCookie = new Cookie("uName",username);

Cookie類提供了一些方法,常用方法如表2-1所示。

表2-1 Cookie類常用方法

創建完成的Cookie對象,可以使用HttpServletResponse的addCookie()方法將其發送到客戶端。addCookie()方法接收一個Cookie類型的值,例如:

    // 將userCookie發送到客戶端
    response.addCookie(userCookie);

使用HttpServletRequest的getCookies()方法可以從客戶端獲得這個網站的所有Cookie,該方法返回一個包含本站所有Cookie的數組,遍歷該數組可以獲得對應的Cookie,例如:

    Cookie[] cookies = request.getCookies();

默認情況下,Cookie在客戶端是保存在內存中的,如果瀏覽器關閉,Cookie也就失效了。如果想要讓Cookie長久地保存在磁盤上,通過使用表2-1中的setMaxAge()方法設置其過期時間,如將客戶端的Cookie的過期時間設置為1周,其示例代碼如下:

    // 在客戶端保存一周
    userCookie.setMaxAge(7*24*60*60);

注意 Cookie的保存位置在不同的操作系統下是不同的,其中Windows XP系統下其保存位置是在C:\Documents and Settings\當前系統用戶名\Local Settings\temporary internet files文件夾中。

2.2.2 Cookie示例

下述代碼用于實現任務描述2.D.1,使用Cookie保存用戶名和密碼,當用戶再次登錄時在相應的文本框中顯示上次登錄時輸入的信息。(1)編寫用于接收用戶輸入的HTML表單文件,在該例子中,沒有使用HTML文件而是使用一個Servlet來完成此功能,這是因為需要通過Servlet去讀取客戶端的Cookie,而HTML文件無法完成此功能。

【描述2.D.1】 LoginServlet.java

    public class LoginServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String cookieName = "userName";
            String cookiePwd = "pwd";
            // 獲得所有cookie
            Cookie[] cookies = request.getCookies();
            String userName = "";
            String pwd = "";
            String isChecked = "";
            // 如果cookie數組不為null,說明曾經設置過
            // 也就是曾經登錄過,那么取出上次登錄的用戶名、密碼
            if (cookies != null) {
                // 如果曾經設置過cookie,checkbox狀態應該是checked
                isChecked = "checked";
                for (int i = 0; i < cookies.length; i++) {
                    // 取出登錄名
                    if (cookies[i].getName().equals(cookieName)) {
                        userName = cookies[i].getValue();
                    }
                    // 取出密碼
                    if (cookies[i].getName().equals(cookiePwd)) {
                        pwd = cookies[i].getValue();
                    }
                }
            }
            response.setContentType("text/html;charset=GBK");
            PrintWriter out = response.getWriter();
            out.println("<html>\n");
            out.println("<head><title>登錄</title></head>\n");
            out.println("<body>\n");
            out.println("<center>\n");
            out.println("    <form action='CookieTest'" + " method='post'>\n");
            out.println("姓名:<input type='text'" + " name='UserName' value='"
                    + userName + "'><br/>\n");
            out.println("密碼:<input type='password' name='Pwd' value='" + pwd
                    + "'><br/>\n");
            out.println("保存用戶名和密碼<input type='checkbox'"
                    + "name='SaveCookie' value='Yes' " + isChecked + ">\n");
            out.println("         <br/>\n");
            out.println("         <input type=\"submit\">\n");
            out.println("    </form>\n");
            out.println("</center>\n");
            out.println("</body>\n");
            out.println("</html>\n");
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    }

上述代碼中,首先使用request.getCookies()獲取客戶端cookies數組;再遍歷該數組,找到對應的Cookie,取出用戶名和密碼;最后將信息顯示在相應的表單控件中。

(2)編寫CookieTest.java程序。

【描述2.D.1】 CookieTest.java

    public class CookieTest extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            Cookie userCookie = new Cookie("userName", request
                    .getParameter("UserName"));
            Cookie pwdCookie = new Cookie("pwd", request.getParameter("Pwd"));
            if (request.getParameter("SaveCookie") != null
                    && request.getParameter("SaveCookie").equals("Yes")) {
                userCookie.setMaxAge(7 * 24 * 60 * 60);
                pwdCookie.setMaxAge(7 * 24 * 60 * 60);
            } else {
                // 刪除客戶端對應的Cookie
                userCookie.setMaxAge(0);
                pwdCookie.setMaxAge(0);
            }
            response.addCookie(userCookie);
            response.addCookie(pwdCookie);
            PrintWriter out = response.getWriter();
            out.println("Welcome," + request.getParameter("UserName"));
        }

        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    }

上述代碼中,首先創建兩個Cookie對象,分別用來儲存表單中傳遞過來的登錄名和密碼,然后根據客戶端的“SaveCookie”元素的值,決定是否向客戶端發送Cookie,或者刪除以前存儲的Cookie。

啟動Tomcat,在IE中訪問http://localhost:8080/ch02/LoginServlet,運行結果如圖2-1所示。

輸入姓名和密碼,選中相應的保存復選框,單擊提交按鈕,顯示結果如圖2-2所示。

圖2-1 第一次訪問LoginServlet

圖2-2 CookieTest結果

當再次登錄時,用戶名和密碼已顯示,如圖2-3所示。

圖2-3 再次訪問LoginServlet

2.3 Session

使用Cookie可以將請求的狀態信息傳遞到下一次請求中(如任務描述2.D.1),但是如果傳遞的狀態信息較多,將極大地降低網絡傳輸效率,并且會增大服務器端程序的處理難度,為此各種服務器端技術都提供了一種將會話狀態保存在服務器端的方案,即Session(會話)技術。

Session是在Java Servlet API中引入的一個非常重要的機制,用于跟蹤客戶端的狀態,即在一段時間內,單個客戶端與Web服務器之間的一連串的交互過程稱為一個會話。

HttpSession是Java Servlet API中提供的對Session機制的實現規范,它僅僅是個接口,Servlet容器必須實現這個接口。當一個Session開始時,Servlet容器會創建一個HttpSession對象,并同時在內存中為其開辟一個空間,用來存放此Session對應的狀態信息。Servlet容器為每一個HttpSession對象分配一個唯一的標識符,稱為SessionID,同時將SessionID發送到客戶端,由瀏覽器負責保存此SessionID。這樣,當客戶端再發送請求時,瀏覽器會同時發送SessionID,Servlet容器可以從請求對象中讀取SessionID,根據SessionID的值找到相應的HttpSession對象。每個客戶端對應于服務器端的一個HttpSession對象,這通過SessionID區分。Session機制如圖2-4所示。

圖2-4 Session機制

注意 通常服務器借助于Cookie把SessionID存儲在瀏覽器進程中,在該瀏覽器進程下一次訪問服務器時,服務器就可以從請求中的Cookie里獲取SessionID。此外,Session還可以借助URL重寫的方式在客戶端保存SessionID。

2.3.1 Session創建

Servlet容器根據HttpServletRequest對象中提供的SessionID,可以找到對應的HttpSession對象。在HttpServletRequest中提供了以下兩種方法來獲取HttpSession:

■ getSession()方法取得請求所在的會話,如果該會話對象不存在則創建一個新會話。

■ getSession(boolean create)返回當前請求的會話。如果當前請求不屬于任何會話,而且create參數為true,則創建一個會話,否則返回null。此后所有來自同一個對象的請求都屬于這個會話,通過它的getSession(false)返回的是當前會話。

例如,可以使用下面兩種方式獲取當前Session:

    HttpSession session=request.getSession();       //獲取當前Session

    HttpSession session=request.getSession(true);  //獲取當前Session

2.3.2 Session使用

HttpSession中定義了如表2-2所示的方法。

表2-2 HttpSession常用方法

其中,用于存取數據的方法有以下兩個。

setAttribute():用于在Session對象中保存數據,數據以Key/Value映射形式存放。

getAttribute():從Session中提取指定Key對應的Value值。

向Session對象中保存數據的示例代碼如下:

    // 將username保存到Session中,并指定其引用名稱為uName
    session.setAttribute("uName", username);

從Session中提取存放的信息,則代碼如下:

    // 取出數據,注意:因該方法的返回數據類型為Object,所以需要轉換數據類型
    String username = (String)session.getAttribute("uName");

用于銷毀Session的方法如下。

invalidate():調用此方法可以同時刪除HttpSession對象和數據。

使用invalidate()銷毀Session的示例代碼如下:

    // 銷毀Session(常用于用戶注銷)
    session.invalidate();

注意 有3種情況可以結束Session:

(1)關閉瀏覽器,Session關閉。

(2)調用HttpSession的invalidate()方法。

(3)兩次訪問時間間隔大于Session定義的非活動時間間隔。

2.3.3 Session生命周期

Session生命周期經過以下幾個過程:

01 客戶端向服務器第一次發送請求的時候,request中并無SessionID。

02 此時服務器會創建一個Session對象,并分配一個SessionID。Session對象保存在服務器端,此時為新建狀態,調用session.isNew()返回true。

03 當服務器端處理完畢后,會將SessionID通過response對象傳回到客戶端,瀏覽器負責保存到當前進程中。

04 當客戶端再次發送請求時,會同時將SessionID發送給服務器。

05 服務器根據傳遞過來的SessionID將這次請求(request)與保存在服務器端的Session對象聯系起來。此時Session已不處于新建狀態,調用session.isNew()返回false。

06 循環執行過程3~5,直到Session超時或銷毀。

Session的生命周期和訪問范圍如圖2-5所示。

圖2-5 Session訪問范圍

在圖2-5中,每個客戶(如Client1)可以訪問多個Servlet,但是一個客戶的多個請求將共享一個Session,同一Web應用下的所有Servlet共享一個ServletContext,即Servlet上下文。ServletContext在本章2.5節將詳細介紹。

2.3.4 Session演示

下述代碼完成任務描述2.D.2,用戶在初始化頁面中輸入一個值,單擊“提交”按鈕,會進入第一個Servlet;第一個Servlet將輸入的值分別保存到request和Session中;在第二個Servlet中從request和Session對象中提取信息并顯示。(1)編寫session.html頁面,代碼如下所示。

【描述2.D.2】session.html

    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=GBK">
    <title>Session示例</title>
    </head>
    <body>
    <center>
    <form method="POST" action="s1">
    <table>
        <tr>
            <td>輸入數據: <input type="text" name="count"></td>
        </tr>
    </table>
    <center><input type="submit" value="提交"></center>
    </form>
    </center>
    </body>
    </html>

在此頁面中,將表單信息提交給第一個名為FirstServlet的Servlet處理。因為第一個Servlet的<url-pattern>是“/s1”,因此表單的action屬性值為“s1”。

(2)編寫第一個Servlet,代碼如下所示。

【描述2.D.2】FirstServlet.java

    public class FirstServlet extends HttpServlet {
        public FirstServlet() {
            super();
        }
        protected void doGet(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
        protected void doPost(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            // 設置請求的編碼字符為GBK
            request.setCharacterEncoding("GBK");
            // 設置響應的文本類型為html,編碼字符為GBK
            response.setContentType("text/html; charset=GBK");
            PrintWriter out = response.getWriter();
            // 獲取表單數據
            String str = request.getParameter("count");
            request.setAttribute("request_param", str);
            HttpSession  session  =  request.getSession();
            session.setAttribute("session_param", str);
            out.println("<a href='s2'>下一頁</a>");
        }
    }

上述代碼中,首先提取表單數據,并分別保存在request和session對象中。再通過下面語句在客戶端瀏覽器中顯示一個超鏈接,單擊此超鏈接,可以鏈接到第二個Servlet(第二個Servlet的<url-pattern>設置為“/s2”)。

    out.println("<a href='s2'>下一頁</a>")

(3)編寫第二個Servlet,代碼如下所示。

【描述2.D.2】SecondServlet.java

    public class SecondServlet extends HttpServlet {
        protected void doGet(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
        protected void doPost(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            Object obj = request.getAttribute("request_param");
            String request_param = null;
            if (obj != null) {
                request_param = obj.toString();
            } else {
                request_param = "null";
            }
            HttpSession  session  =  request.getSession();
            Object obj2 = session.getAttribute("session_param");
            String session_param = null;
            if (obj2 != null) {
                session_param = obj2.toString();
            } else {
                session_param = "null";
            }
            response.setContentType("text/html; charset=GBK");
            PrintWriter out = response.getWriter();
            out.println("<html>");
            out.println("<body >");
            out.println("<h2>請求對象中的參數是 :" + request_param +
            "</h2>");
            out.println("<h2>Session對象中的參數是 :" + session_param
                    + "</h2></body></html>");
        }
    }

上述代碼中,分別從request和session對象中獲取數據并輸出。

啟動Tomcat,在IE中訪問http://localhost:8080/ch02/session.html,運行結果如圖2-6所示。

在文本框中輸入數據,單擊“提交”按鈕,提交給FirstServlet處理,運行結果如圖2-7所示。

圖2-6 session.html

圖2-7 FirstServlet運行結果

單擊“下一頁”超鏈接,進入SecondServlet,運行結果如圖2-8所示。

圖2-8 SecondServlet.java運行結果

如圖2-8所示,保存在request對象中的數據變為null,而保存在Session對象中的數據是正確的。因為在單擊“下一頁”超鏈接后進入第二個Servlet時,上一次的request已經結束,此時是一個新的請求,該request對象中并無保存數據,因此提取的數據只能為null;而這兩次請求位于同一個會話中,Session的生命周期并未結束,因此能夠獲取Session中保存的數據。

在上述任務的執行過程中,服務器在處理客戶端的請求時創建了新的HttpSession對象,將會話標識號(SessionID)作為一個Cookie項加入到響應信息中返回給客戶端。瀏覽器再次發送請求時,服務器程序從Cookie中找到SessionID,就可以檢索到已經為該客戶端創建了的HttpSession對象,而不必再創建新的對象,通過這種方式就實現了對同一個客戶端的會話狀態跟蹤。

注意 使用Cookie實現Session跟蹤時,默認情況下Cookie保存在瀏覽器進程使用的內存中,并沒有保存到磁盤上,因此瀏覽器關閉后,Cookie內容消失,相應的SessionID也就不再存在,所以再次打開瀏覽器發送請求時,已無法發送上次的SessionID,也就無法在服務器端關聯上次的HttpSession對象了。

2.4 URL重寫

有時,用戶由于某些原因禁止了瀏覽器的Cookie功能,Servlet規范中還引入了一種補充的會話管理機制,它允許不支持Cookie的瀏覽器也可以與Web服務器保持連續的會話。這種補充機制要求在需要加入同一會話的每個URL后附加一個特殊參數,其值為會話標識號(SessionID)。當用戶單擊響應消息中的超鏈接發出下一次請求時,如果請求消息中沒有包含Cookie頭字段,Servlet容器則認為瀏覽器不支持Cookie,它將根據請求URL參數中的SessionID來實施會話跟蹤。將SessionID以參數形式附加在URL地址后的技術稱為URL重寫。

HttpServletResponse接口中定義了兩個用于完成URL重寫的方法。

encodeURL():用于對超鏈接或Form表單的action屬性中設置的URL進行重寫。

encodeRedirectURL():用于對要傳遞給HttpServletResponse.sendRedirect()方法的URL進行重寫。

注意 encodeURL()和encodeRedirectURL()方法根據請求消息中是否包含Cookie頭字段來決定是否進行URL重寫。

下述步驟用于實現任務描述2.D.3,基于任務描述2.D.2,通過URL重寫來實現Session會話技術。

(1)修改描述2.D.2中FirstServlet.java代碼片段,使用encodeURL()方法對下述代碼進行重寫。

    out.println("<a href='s2'>下一頁</a>");

改為:

    out.println("<a href=" + response.encodeURL("s2") + ">下一頁</a>");

(2)禁用IE的Cookie功能,重新啟動IE,訪問http://localhost:8080/ch02/session.html,查看網頁源文件,可以觀察到超鏈接內容如下:

    <a href=s2;jsessionid=EE7406414267CAC832A2A08B2FCC1A7E>下一頁</a>

通過單擊URL重寫后的超鏈接,服務器能夠識別同一瀏覽器發出的請求,從而實現了會話功能。

注意 由于Tomcat發送給瀏覽器的SessionID的Cookie名稱為jsessionid,因此,Tomcat服務器中的URL重寫就是在URL中附加了jsessionid參數,其值為SessionID的值。

2.5 ServletContext接口

Servlet上下文是運行Servlet的邏輯容器。同一個上下文中的所有Servlet共享存于其中的信息和屬性。在Servlet API中定義了一個ServletContext接口,用于存取Servlet運行的環境或者上下文信息。ServletContext對象可以通過使用ServletConfig對象的getServletContext()方法獲得,在Servlet中提供了getServletContext()方法也可以直接獲得ServletContext對象。

2.5.1 ServletContext的方法

ServletContext接口中定義了許多有用的方法,如表2-3所示。

表2-3 ServletContext方法列表

其中getAttribute()、setAttribute()、removeAttribute()和getInitParameter()是在Web開發中比較常用的方法,具體的使用方法會在本章后續內容的示例中講解。

2.5.2 ServletContext的生命周期

ServletContext的生命周期過程如下:

01 新Servlet容器啟動的時候,服務器端會創建一個ServletContext對象。

02 在容器運行期間ServletContext對象一直存在。

03 當容器停止時,ServletContext的生命周期結束。

2.5.3 ServletContext示例

下述步驟用于實現任務描述2.D.4,使用Servlet上下文保存訪問人數。(1)IndexServlet是所有客戶端訪問網站時首先需要訪問的Servlet。每當有一個客戶訪問該Servlet時,人數將加1,并且保存到Servlet上下文中,這樣,在此應用中的任何程序都可以訪問到該計數器的值。

【描述2.D.3】IndexServlet.java

    public class IndexServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            ServletContext  ctx  =  this.getServletContext();
            synchronized (this) {
                Integer  counter  =  (Integer)  ctx.getAttribute("UserNumber");
                int tmp = 0;
                // 如果counter為null,
                // 說明servlet上下文中還沒有設置UserNumber屬性
                // 此次訪問為第一次訪問
                if (counter == null) {
                    counter = new Integer(1);
                } else {
                    // 取出原來計數器的值加上1
                    tmp = counter.intValue() + 1;
                    counter = new Integer(tmp);
                }
                ctx.setAttribute("UserNumber",  counter);
            }
            response.setContentType("text/html;charset=GBK");
            PrintWriter out = response.getWriter();
            out.println("<HTML>");
            out.println("<HEAD><TITLE>首頁</TITLE></HEAD>");
            out.println("<BODY>");
            out.println("這是第一頁<BR>");
            out.println("<a href='UserNumber'>人數統計</a>");
            out.println("</BODY></HTML>");
        }
    }

在上述代碼中,首先通過HttpServlet的getServletContext()方法獲得對應的ServletContext對象。然后,通過ServletContext的getAttribute()方法讀取名為“UserNumber”的屬性值,如果這個屬性值不存在(返回值null),說明“UserNumber”還沒有被設置,此次訪問為第一次訪問,否則,將其中的計數值讀取出來加上1,再寫回到上下文中。另外,為了防止多個客戶同時訪問這個Servlet引起數據不同步問題,此處使用synchronized進行了同步控制。

(2)在另一個Servlet程序UserNumber.java中讀取保存在Servlet上下文中的數據。

【描述2.D.3】UserNumber.java

    public class UserNumber extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            ServletContext  ctx  =  this.getServletContext();
            Integer  counter  =  (Integer)  ctx.getAttribute("UserNumber");

            response.setContentType("text/html;charset=GBK");
            PrintWriter out = response.getWriter();
            out.println("<HTML>");
            out.println("<HEAD><TITLE>訪問人數統計</TITLE></HEAD>");
            out.println("<BODY>");

            if (counter != null) {
                out.println("已經有" + counter.intValue() + "人次訪問本網站!");
            } else {
                out.println("你是第一個訪問本網站的!");
            }
            out.println("</BODY></HTML>");
        }
    }

在上述代碼中,也是先通過HttpServlet的getServletContext()方法獲得對應的Servlet上下文對象;然后通過ServletContext對象的getAttribute()方法來獲得計數器的值,并且將它輸出到客戶端。

啟動Tomcat,在IE中訪問http://localhost:8080/ch02/IndexServlet,運行結果如圖2-9所示。

單擊“人數統計”超鏈接,查看人數,運行結果如圖2-10所示。

再新打開兩個IE進程窗口,進行訪問,運行結果如圖2-11所示。

圖2-9 訪問IndexServlet

圖2-10 訪問統計

圖2-11 訪問統計

2.5.4 初始化參數和ServletConfig

ServletContext中除了存取和Web應用全局相關的屬性外,還可以通過getInitParameter()方法獲得設置在web.xml中的初始化參數。

下述代碼用于實現任務描述2.D.4,訪問web.xml中的初始化參數信息。

(1)在web.xml中設置參數信息。

【描述2.D.4】在web.xml中配置初始化參數

    <web-app>
    <!-- 初始化參數 -->
        <context-param>
            <!-- 參數名 -->
            <param-name>serverName</param-name>
            <!-- 參數值 -->
            <param-value>localhost</param-value>
        </context-param>
        <context-param>
            <param-name>dbInstance</param-name>
            <param-value>nitpro</param-value>
        </context-param>
        <context-param>
            <param-name>userName</param-name>
            <param-value>system</param-value>
        </context-param>
        <context-param>
            <param-name>userPwd</param-name>
            <param-value>manager</param-value>
        </context-param>
    <!--其他配置-->
    </web-app>

在該web.xml中,設置了4個全局初始化參數,它們的名字為“serverName”、“dbInstance”、“userName”和“userPwd”。

(2)在Servlet中訪問web.xml初始化參數,并輸出。

【描述2.D.4】InitParamServlet.java

    public class InitParamServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html;charset=GBK");
            PrintWriter out = response.getWriter();
            // 獲得ServletContext對象
            ServletContext  ctx  =  this.getServletContext();
            // 獲得web.xml中設置的初始化參數
            String  serverName   =  ctx.getInitParameter("serverName");
            String  dbInstance   =  ctx.getInitParameter("dbInstance");
            String  userName  =  ctx.getInitParameter("userName");
            String  password  =  ctx.getInitParameter("userPwd");
            out.println("<HTML>");
            out.println("<HEAD><TITLE>");
            out.println("讀取初始化參數</TITLE></HEAD>");
            out.println("<BODY>");
            out.println("服務器:" + serverName + "<br>");
            out.println("數據庫實例:" + dbInstance + "<br>");
            out.println("用戶名稱:" + userName + "<br>");
            out.println("用戶密碼:" + password + "<br>");
            out.println("</BODY></HTML>");
        }
    }

在上述代碼中,通過使用ServletContext對象的getInitParameter()方法獲得在web.xml中設置的初始化參數。

啟動Tomcat,在IE中訪問http://localhost:8080/ch02/InitParamServlet,運行結果如圖2-12所示。

圖2-12 讀取初始化參數

初始化參數也可以通過使用ServletConfig對象中的getInitParameter()方法來獲得,例如:

    ServletConfig sc = getServletConfig();
    name = sc.getInitParameter("userName ");

注意 Servlet容器初始化一個Servlet對象時,會為該Servlet對象分配一個ServletConfig對象。ServletConfig對象包含Servlet的初始化參數信息,它與ServletContext關聯。ServletConfig和ServletContext主要區別如下:

(1)ServletConfig作用于某個特定的Servlet。

(2)ServletContext作用于整個Web應用,是所有Servlet的上下文環境。

小結

通過本章的學習,學生應該能夠學會:

■ Cookie是保存在客戶端的小段文本。

■ 通過請求可以獲得Cookie,通過響應可以寫入Cookie。

■ Session是瀏覽器與服務器之間的一次通話,它包含瀏覽器與服務器之間的多次請求、響應過程。

■ Session可以在用戶訪問一個Web站點的多個頁面時共享信息。

■ 在Servlet中通過request.getSession()獲取當前Session對象。

■ 關閉瀏覽器、調用Session的invalidate()方法或者等待Session超時都可以使Session失效。

■ HttpSession使用getAttribute()和setAttribute()方法讀寫數據。

■ ServletContext是運行Servlet的容器。

■ 在Servlet中可通過getServletContext()方法獲取ServletContext實例。

■ ServletContext使用getAttribute()和setAttribute()方法讀寫數據。

練習

1. 下列關于Cookie的說法正確的是______。(多選)

A. Cookie保存在客戶端

B. Cookie可以被服務器端程序修改

C. Cookie中可以保存任意長度的文本

D. 瀏覽器可以關閉Cookie功能

2. 寫入和讀取Cookie的代碼分別是______。

A. request.addCookies()和response.getCookies()

B. response.addCookie()和request.getCookie()

C. response.addCookies()和request.getCookies()

D. response.addCookie()和request.getCookies()

3. Tomcat的默認端口號是______。

A. 80

B. 8080

C. 8088

D. 8000

4. HttpServletRequest的______方法可以得到會話。(多選)

A. getSession()

B. getSession(boolean)

C. getRequestSession()

D. getHttpSession()

5. 下列選項中可以關閉會話的是______。(多選)

A. 調用HttpSession的close()方法

B. 調用HttpSession的invalidate()方法

C. 等待HttpSession超時

D. 調用HttpServletRequest的getSession(false)方法

6. 在HttpSession中寫入和讀取數據的方法是______。

A. setParameter()和getParameter()

B. setAttribute()和getAttribute()

C. addAttribute()和getAttribute()

D. set()和get()

7. 下列關于ServletContext的說法正確的是______。(多選)

A. 一個應用對應一個ServletContext

B. ServletContext的范圍比Session的范圍要大

C. 第一個會話在ServletContext中保存了數據,第二個會話讀取不到這些數據

D. ServletContext使用setAttribute()和getAttribute()方法操作數據

8. 關于HttpSession的getAttribute()和setAttribute()方法,正確的說法是______。(多選)

A. getAttribute()方法返回類型是String

B. getAttribute()方法返回類型是Object

C. setAttribute()方法保存數據時如果名字重復會拋出異常

D. setAttribute()方法保存數據時如果名字重復會覆蓋以前的數據

9. 使HttpSession失效的三種方式是______、______和______。

10. 測試在其他瀏覽器下Session的生命周期,如Firefox、Chrome等。

主站蜘蛛池模板: 忻城县| 双柏县| 广宁县| 兰坪| 峡江县| 新化县| 连江县| 金湖县| 安国市| 麦盖提县| 施秉县| 邢台县| 元阳县| 阳朔县| 靖远县| 广德县| 元江| 探索| 宜兰县| 印江| 崇信县| 绥江县| 咸丰县| 南澳县| 平阴县| 黄浦区| 西青区| 民勤县| 青州市| 喀喇| 湖州市| 鄂托克前旗| 平陆县| 丽江市| 吐鲁番市| 平果县| 保亭| 武平县| 商河县| 禹城市| 法库县|