天天看點

Servlet的學習之Session(2)

  在上一篇中我們學習了Session對象預設在一個會話過程中,由伺服器建立,能儲存在這個會話過程中使用者通路多個web資源時産生的需要儲存的資料,并在通路伺服器中其他web資源時可以将這些資料從Session中取出并為之使用。

  然而在一個會話過程後就将無法再取到Session對象(因為cookie不存在了,原因下面将闡明),可能導緻的是使用者誤将浏覽器關閉而使儲存在Session中的資料無法再取到,是以如果我們能将多個會話同時使用同一個Session對象就能滿足使用者“刁蠻”的需求。

  在前一篇的最開始就說過,在預設情況下,一個浏覽器獨享一個Session。請注意,這裡是預設情況下, 而我們有了新需求,就要打破預設情況(比如預設情況下Cookie也是在一個會話結束後消失,但是我們設定了setMaxAge後就能在硬碟中生存),做到多個浏覽器共享一個Session,同時這也意味着一台使用者主機有一個Session,而不管他開了多少個浏覽器。

  那麼首先我們必須先了解Session的底層工作原理。我們還是先看在一個會話過程中,同一個浏覽器在通路多個web資源的情況好了,大緻分為以下幾個步驟:

  1,浏覽器通路某個Servlet,這時如果伺服器要從請求對象中擷取Session對象(第一次擷取也是建立),那麼伺服器會為這個Session對象建立一個id:JSESSIONID

  2,同時在對浏覽器的響應過程中,這個Session會将JSESSIONID這個id以Cookie形式回送給用戶端浏覽器,記住,這時候Cookie伺服器沒有設定有效時間,是以是存在浏覽器的緩存中,而不是在硬碟檔案。

  3,當使用者繼續在這個會話過程中通路其他Servlet,這時候這個Servlet再從請求對象中擷取Session對象,注意這時候擷取Session對象是從浏覽器發來的請求中查詢是否有名為JSESSIONID的這個Cookie,如果有,那麼這個Session就不用再建立,而是直接根據查詢伺服器中這個相同JSESSIONID值的Session,換句話說就可以取得之前存在這個Session中的資料。

  總結來說,Session是基于Cookie的。

  (注:cookie并不是萬能的,Session首先是依據cookie,但是有時候cookie不能用,這時候Session會查詢發來請求的URL位址是否有JSESSIONID,具體請看《Servlet的學習之Session(3)》。)

  我們可以通過下面這個動圖來看一看:

  

Servlet的學習之Session(2)

  對于這個Session的隐藏Cookie,我們可以做個小實驗來驗證下,在【myservlet】這個web工程下建立兩個Servlet,分别命名為SessionDemo1和SessionDemo2:

在SessionDemo1代碼為:

1     HttpSession session = request.getSession();
2     String data = "Message from SessionDemo1";
3     session.setAttribute("data", data);      

在SessionDemo2代碼為:

1     HttpSession session = request.getSession();
2     System.out.println((String)session.getAttribute("data"));      

我們在浏覽器中打開HttpWatch,來通路SessionDemo1,因為是首次通路Servlet,檢視SessionDemo1給浏覽器的響應:

Servlet的學習之Session(2)

确确實實伺服器發送回浏覽器有這個JSESSIONID名稱的Cookie,這時候如果我們再在打開的浏覽器去通路SessionDemo2,那麼在HttpWatch中觀察請求包的内容發現:

Servlet的學習之Session(2)

再次通路伺服器時,浏覽器就會帶着這個名為JSESSIONID的Cookie給伺服器,伺服器正是通過這個cookie中的JSESSIONID值去伺服器中查找之前為該浏覽器建立的Session。

  如果我們将浏覽器關閉,由于這個cookie沒有設定“setMaxAge”,是以這個cookie隻存在于浏覽器的緩沖,浏覽器關閉即被銷毀。如果想使關閉浏覽器之後,Session還能存在,我們就要人為的覆寫這個Session的cookie,并設定覆寫cookie的有效時間和有效路徑。而這個cookie的值,也就是JSESSIONID的值,可以通過Session的getId()方法得到。

  1,覆寫有效時間:

  注意,伺服器在為浏覽器建立Session後,在使用者沒有操作的情況下(或者浏覽器關閉後)預設為其維護30分鐘。這點可以從Tomcat的【web.xml】檔案中可以看出:

Servlet的學習之Session(2)

當然我們從這裡也可以修改伺服器預設的銷毀無操作的Session時間。

  當然如果我們不要全局設定所有伺服器中Session的銷毀時間,就在每個web應用中的web.xml檔案中自定義添加<session-config>和<session-timeout>進行設定。

  注:我們還可以通過Session對象的invalidate()方法,将某個Session進行立刻銷毀。

  對此,如果我們要覆寫一個Session的cookie并儲存在硬碟檔案中,我們設定的cookie有效時間就不要超過伺服器預設的session-timeout時間。

  2,覆寫有效路徑:

  我們在《Servlet的學習之Cookie》中知道,如果我們建立一個Cookie對象,沒有設定“setPath”,那麼Cookie的有效路徑為建立該Cookie的程式(通常為某個Servlet),即隻有通路了這個程式時浏覽器才會帶着Cookie過去,那實在是“人脈不通”,通路這個web應用的其他資源就無法再使用Session了。

  我們看看剛才的第一次通路Servlet時,伺服器為浏覽器建立的Session中的cookie的有效路徑:

Servlet的學習之Session(2)

可以看到這個伺服器預設将JSESSIONID這個cookie的有效路徑設定為建立這個Session的web工程根目錄。是以我們要覆寫Session中的cookie時也應該設定路徑為該web工程根目錄。

  好,接下來對上面那個Servlet的例子進行改造,我們隻需要在SessionDemo1中修改就行,因為這個首次将Session的cookie傳回給用戶端,修改後代碼如下:

1     HttpSession session = request.getSession();
2     String data = "Message from SessionDemo1";
3     session.setAttribute("data", data);
4         
5     Cookie cookie = new Cookie("JSESSIONID", session.getId());
6     cookie.setMaxAge(30*60);
7     cookie.setPath("/myservlet");
8     response.addCookie(cookie);      

這樣,當我們打開浏覽器通路了SessionDemo1之後,就能在存放cookie的目錄中找到該cookie,如果我們通過HttpWatch來檢視可以看到重名的這個cookie:

Servlet的學習之Session(2)

雖然JSEESIONID這個cookie重名了,沒有關系,因為其值都是一樣的,并且如果我們将浏覽器關閉後,沒有設定cookie有效時間的(也是原先Session發來的)cookie将不複存在(存在浏覽器緩存中,浏覽器關閉就被銷毀),這時重新打開一個浏覽器,再去通路SessionDemo2依然能擷取到原來Session中儲存的内容:

Servlet的學習之Session(2)

注意,這是另外打開浏覽器視窗通路的SessionDemo2!!另附:

Servlet的學習之Session(2)

  通過這裡我們可以看到,我們人為地将原先Session定義的cookie給替換了,而Session并不知道,隻要能獲得“JSESSIONID”這個cookie,它就認為cookie是存在的,可以從這個cookie中id值擷取以前儲存的資訊,是以我們實作了一台主機共享一個Session,此時,當浏覽器關閉,或者說結束一個會話後,依然能擷取Session來擷取之前儲存的資料。

繼續閱讀