jsp Servlet基礎入門學習:工作階段狀態 
                
                    
                        最後更新:2017-02-28 
                        來源:互聯網 
                        上載者:User 
                     
                                                         
                
                js|servlet  
   10.1 工作階段狀態概述  
 
    HTTP協議的“無狀態”(Stateless)特點帶來了一系列的問題。特別是通過線上商店購物時,伺服器不能順利地記住以前的事務就成了嚴重的問題。它使得“購物籃”之類的應用很難實現:當我們把商品加入購物籃時,伺服器如何才能知道籃子裡原先有些什嗎?即使伺服器儲存了上下文資訊,我們仍舊會在電子商務應用中遇到問題。例如,當使用者從選擇商品的頁面(由普通的伺服器提供)轉到輸入信用卡號和送達地址的頁面(由支援SSL的安全伺服器提供),伺服器如何才能記住使用者買了些什嗎? 
 
    這個問題一般有三種解決方案: 
 
 Cookie。利用HTTP Cookie來儲存有關購物會話的資訊,後繼的各個串連可以查看當前會話,然後從伺服器的某些地方提取有關該會話的完整資訊。這是一種優秀的,也是應用最廣泛的方法。然而,即使Servlet提供了一個進階的、使用方便的Cookie介面,仍舊有一些繁瑣的細節問題需要處理: 
 從其他Cookie中分別出儲存會話標識的Cookie。 
 為Cookie設定合適的作廢時間(例如,停機時間超過24小時的會話一般應重設)。 
 把會話標識和伺服器端相應的資訊關聯起來。(實際儲存的資訊可能要遠遠超過儲存到Cookie的資訊,而且象信用卡號等敏感資訊永遠不應該用Cookie來儲存。) 
 改寫URL。你可以把一些標識會話的資料附加到每個URL的後面,伺服器能夠把該會話標識和它所儲存的會話資料關聯起來。這也是一個很好的方法,而且還有當瀏覽器不支援Cookie或使用者已經禁用Cookie的情況下也有效這一優點。然而,大部分使用Cookie時所面臨的問題同樣存在,即伺服器端的程式要進行許多簡單但單調冗長的處理。另外,還必須十分小心地保證每個URL後面都附加了必要的資訊(包括非直接的,如通過Location給出的重新導向URL)。如果使用者結束會話之後又通過書籤返回,則會話資訊會丟失。 
 隱藏表單域。HTML表單中可以包含下面這樣的輸入欄位:<INPUT TYPE="HIDDEN" NAME="session" VALUE="...">。這意味著,當表單被提交時,隱藏欄位的名字和資料也被包含到GET或POST資料裡,我們可以利用這一機制來維持會話資訊。然而,這種方法有一個很大的缺點,它要求所有頁面都是動態產生的,因為整個問題的核心就是每個會話都要有一個唯一識別碼。 
    Servlet為我們提供了一種與眾不同的方案:HttpSession API。HttpSession API是一個基於Cookie或者URL改寫機制的進階工作階段狀態跟蹤介面:如果瀏覽器支援Cookie,則使用Cookie;如果瀏覽器不支援Cookie或者Cookie功能被關閉,則自動使用URL改寫方法。Servlet開發人員無需關心細節問題,也無需直接處理Cookie或附加到URL後面的資訊,API自動為Servlet開發人員提供一個可以方便地儲存會話資訊的地方。 
 
    
10.2 工作階段狀態跟蹤API  
    在Servlet中使用會話資訊是相當簡單的,主要的操作包括:查看和當前請求關聯的會話對象,必要的時候建立新的會話對象,查看與某個會話相關的資訊,在會話對象中儲存資訊,以及會話完成或中止時釋放會話對象。 
 
    10.2.1 查看當前請求的會話對象 
 
    查看當前請求的會話對象通過調用HttpServletRequest的getSession方法實現。如果getSession方法返回null,你可以建立一個新的會話對象。但更經常地,我們通過指定參數使得不存在現成的會話時自動建立一個會話對象,即指定getSession的參數為true。因此,訪問當前請求會話對象的第一個步驟通常如下所示: 
 HttpSession session = request.getSession(true);
 
    10.2.2 查看和會話有關的資訊 
 
    HttpSession對象生存在伺服器上,通過Cookie或者URL這類後台機制自動關聯到請求的寄件者。會話對象提供一個內建的資料結構,在這個結構中可以儲存任意數量的鍵-值對。在2.1或者更早版本的Servlet API中,查看以前儲存的資料使用的是getValue("key")方法。getValue返回Object,因此你必須把它轉換成更加具體的資料類型。如果參數中指定的鍵不存在,getValue返回null。 
 
    API 2.2版推薦用getAttribute來代替getValue,這不僅是因為getAttribute和setAttribute的名字更加匹配(和getValue匹配的是putValue,而不是setValue),同時也因為setAttribute允許使用一個附屬的HttpSessionBindingListener 來監視數值,而putValue則不能。 
 
    但是,由於目前還只有很少的商業Servlet引擎支援2.2,下面的例子中我們仍舊使用getValue。這是一個很典型的例子,假定ShoppingCart是一個儲存已購買商品資訊的類: 
 
 HttpSession session = request.getSession(true);
 ShoppingCart previousItems = 
 (ShoppingCart)session.getValue("previousItems");
 if (previousItems != null) {
 doSomethingWith(previousItems);
 } else {
 previousItems = new ShoppingCart(...);
 doSomethingElseWith(previousItems);
 }
 
    大多數時候我們都是根據特定的名字尋找與它關聯的值,但也可以調用getValueNames得到所有屬性的名字。getValuesNames返回的是一個String數組。API 2.2版推薦使用getAttributeNames,這不僅是因為其名字更好,而且因為它返回的是一個Enumeration,和其他方法(比如HttpServletRequest的getHeaders和getParameterNames)更加一致。 
 
    雖然開發人員最為關心的往往是儲存到會話對象的資料,但還有其他一些資訊有時也很有用。 
 
 getID:該方法返回會話的唯一標識。有時該標識被作為鍵-值對中的鍵使用,比如會話中只儲存一個值時,或儲存上一次會話資訊時。 
 isNew:如果客戶(瀏覽器)還沒有綁定到會話則返回true,通常意味著該會話剛剛建立,而不是引用自用戶端的請求。對於早就存在的會話,傳回值為false。 
 getCreationTime:該方法返回建立會話的以毫秒計的時間,從1970.01.01(GMT)算起。要得到用於列印輸出的時間值,可以把該值傳遞給Date建構函式,或者GregorianCalendar的setTimeInMillis方法。 
 getLastAccessedTime:該方法返回客戶最後一次發送請求的以毫秒計的時間,從1970.01.01(GMT)算起。 
 getMaxInactiveInterval:返回以秒計的最大時間間隔,如果客戶請求之間的間隔不超過該值,Servlet引擎將保持會話有效。負數表示會話永遠不會逾時。 
    10.2.3 在會話對象中儲存資料 
 
    如上節所述,讀取儲存在會話中的資訊使用的是getValue方法(或,對於2.2版的Servlet規範,使用getAttribute)。儲存資料使用putValue(或setAttribute)方法,並指定鍵和相應的值。注意putValue將替換任何已有的值。有時候這正是我們所需要的(如下例中的referringPage),但有時我們卻需要提取原來的值並擴充它(如下例previousItems)。範例程式碼如下: 
 
 HttpSession session = request.getSession(true);
 session.putValue("referringPage", request.getHeader("Referer"));
 ShoppingCart previousItems = 
 (ShoppingCart)session.getValue("previousItems");
 if (previousItems == null) {
 previousItems = new ShoppingCart(...);
 }
 String itemID = request.getParameter("itemID");
 previousItems.addEntry(Catalog.getEntry(itemID));
 
 session.putValue("previousItems", previousItems);
 
    
10.3 執行個體:顯示會話資訊  
 
    下面這個例子產生一個Web頁面,並在該頁面中顯示有關當前會話的資訊。 
 
 package hall;
 
 import java.io.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 import java.net.*;
 import java.util.*;
 
 public class ShowSession extends HttpServlet {
 public void doGet(HttpServletRequest request,
 HttpServletResponse response)
 throws ServletException, IOException {
 HttpSession session = request.getSession(true);
 response.setContentType("text/html");
 PrintWriter out = response.getWriter();
 String title = "Searching the Web";
 String heading;
 Integer accessCount = new Integer(0);;
 if (session.isNew()) {
 heading = "Welcome, Newcomer";
 } else {
 heading = "Welcome Back";
 Integer oldAccessCount =
 // 在Servlet API 2.2中使用getAttribute而不是getValue
 (Integer)session.getValue("accessCount"); 
 if (oldAccessCount != null) {
 accessCount =
 new Integer(oldAccessCount.intValue() + 1);
 }
 }
 // 在Servlet API 2.2中使用putAttribute
 session.putValue("accessCount", accessCount); 
 
 out.println(ServletUtilities.headWithTitle(title) +
 "<BODY BGCOLOR=\"#FDF5E6\">\n" +
 "<H1 ALIGN=\"CENTER\">" + heading + "</H1>\n" +
 "<H2>Information on Your Session:</H2>\n" +
 "<TABLE BORDER=1 ALIGN=CENTER>\n" +
 "<TR BGCOLOR=\"#FFAD00\">\n" +
 " <TH>Info Type<TH>Value\n" +
 "<TR>\n" +
 " <TD>ID\n" +
 " <TD>" + session.getId() + "\n" +
 "<TR>\n" +
 " <TD>Creation Time\n" +
 " <TD>" + new Date(session.getCreationTime()) + "\n" +
 "<TR>\n" +
 " <TD>Time of Last Access\n" +
 " <TD>" + new Date(session.getLastAccessedTime()) + "\n" +
 "<TR>\n" +
 " <TD>Number of Previous Accesses\n" +
 " <TD>" + accessCount + "\n" +
 "</TABLE>\n" +
 "</BODY></HTML>");
 }
 public void doPost(HttpServletRequest request,
 HttpServletResponse response)
 throws ServletException, IOException {
 doGet(request, response);
 }
 }