正確理解session:使用HttpSession進行會話管理
1. Session模型:
1) HTTP協議是無狀態的,無法記錄多次請求/響應之間的聯絡,而Session模型就好比一箇中間人,可以幫助使用HTTP協議通訊的雙方記錄每次通訊的內容(即會話的內容),因此Session模型就是一種中間人模型,可以儲存通訊記錄;
2) HttpSession對Session模型的實現:
i. 當客戶端第一次請求伺服器時伺服器可以主動建立一個HttpSession物件專門用於記錄該客戶端和自己這一次,包括今後將要發生的多次,通訊記錄,建立HttpSession就是建立會話,這種會話(會話物件)可以長期儲存;
ii. Web容器會為每個會話分配一個唯一的ID號,容器會將相關通訊記錄都儲存在相應ID的會話物件中,並可以將這些會話物件長期儲存在Web容器中(伺服器端);
iii. 而Servlet響應客戶端時只需要將相應會話的ID儲存在Cookie中返回即可,而瀏覽器下次再請求時只需要憑Cookie中的會話物件的ID讓Web容器調出儲存在其中的會話物件即可;
3) 大致模型就是:
客戶端(Cookie:Session ID) <====> 伺服器端(很多Session物件)
!伺服器根據客戶端請求Cookie中的Session ID調出儲存在Web容器中相應的Session物件,然後取出物件中儲存的前幾次通訊的記錄並利用!
2. 使用HttpSession的基本流程:
1) 建立或獲取Session物件要使用HttpServletRequest的getSession函式:
i. HttpSession HttpServletRequest.getSession(); // 如果通訊雙方建立過會話則返回之前儲存在Web容器中的會話,否則就建立一個新的會話
ii. HttpSession HttpServletRequest.getSession(boolean create); // true就和getSession等價,而false在沒有建立過會話的條件下返回null,否則返回相應的會話
!!通常使用前者較多;
2) 插入和提取會話記錄:
i. 插入會話記錄使用HttpSession的setAttribute,提取會話記錄使用getAttribute,用法和HttpServletRequest的setAttribute和getAttribute一模一樣,會話記錄也是以“鍵值對”的形式儲存;
ii. Object HttpSession.getAttribute(String name); // 提取相應引數名的物件
iii. void HttpSession.setAttribute(String name, Object value); // 設定鍵值對
!!會話記錄都是以類物件的形式儲存的,提取出來需要型別轉換一下才能放心使用;
2. 會話模型的具體實現:
1) 在建立Session的時候Web容器會為每個Session分配一個唯一的ID號,因此一個HttpSession物件中儲存有ID號和各個鍵值對(會話記錄);
2) Session物件都儲存在Web容器中;
3) 在響應客戶端時Web容器會自動把本次通訊的Session的ID加入到Cookie中返回給客戶端,其在set-cookie標頭中的引數名為JSESSIONID(在Tomcat中是這樣),例如:set-cookie: k1=v1; k2=v2…JSESSIONID=C8D2S02LA23…kn=vn….
4) 客戶端和伺服器端通訊只傳送Session的ID而不傳送Session中的通訊記錄,當伺服器端收到客戶端請求並呼叫getSession時,就會根據請求Cookie中的Session ID從Web容器快取中調出相應的Session物件,這樣通訊時就能節省很多容量資源,加大網路流量的利用率同時提高效率,只不過通訊記錄都是儲存在伺服器端的,因此伺服器端的儲存壓力較大;
3. HttpSession的常用方法以及配置:
1) 獲取Session的ID:String getId();
2) 設定Session的存活期限:void setMaxInactiveInterval(int interval); // 設定請求間隔時間(單位是秒),當瀏覽器超出interval秒還沒有請求該應用的話就清除該Session(釋放記憶體)
!!如果interval為0或負則表示Session可以無限存活(除非關閉Web容器);
!!由於用伺服器來儲存通訊記錄是非常消耗記憶體資源的,因此建議不要將大型物件儲存在Session中,如果Session中的物件較大那麼設定存活期限就相當必要,否則會導致記憶體不足降低服務效率;
3) 在web.xml中配置Session的預設存活時間:如果沒有呼叫setMaxInactiveInterval則使用web.xml中的預設值,否則就使用程式中設定的值
i. 所有關於Session的配置都放在<web-app>的<session-config>中;
ii. 存活時間的配置標籤是<session-timeout>,但是單位是分鐘!!!和set函式單位是秒不一樣,千萬別搞錯了!!
iii. 示例:
- ...
- <web-app>
- ...
- <session-config>
- <session-timeout>30</session-timeout>
- </session-config>
- ...
- </web-app>
- ...
!!!這裡設定的是30分鐘而不是30秒;
3) Session ID的Cookie配置:
i. 預設情況下,Web容器自動為Session建立的傳送ID的Cookie的max-age(Cookie存活時長)為“退出時長”,“退出時常”是指Cookie的存活時間為從建立到瀏覽器關閉那麼久,一旦瀏覽器退出那麼該Cookie就會被清除,不會儲存到下次瀏覽器開啟;
ii. 因此在預設情況下,瀏覽器退出再開啟後再次請求該網站,那麼請求Cookie中就沒有Session ID的資訊了,那麼伺服器端使用getSession時就沒有ID可以查詢,那麼此時就會建立一個新的Session,因此之前的通訊記錄就無法再獲取了,即使記錄前幾次通訊記錄的Session物件還可能存活在Web容器的快取中,但是由於調出該物件的請求不復存在了,因此該Session也只能深埋在快取中直到達到存活期限被銷燬;
iii. 所以我們希望修改Session ID的Cookie的存活時間,關於Session ID的Cookie的配置可以在web.xml中進行:
a. 在<session-config>標籤下的<cookie-config>中配置即可;
b. <name>標籤可以設定Cookie的名稱,可以將預設的JSESSIONID的名稱改成其它你想要的;
c. <max-age>標籤可以修改Cookie的存活時間,單位為秒;
d. 示例:
- <session-config>
- <session-timeout>30</session-timeout>
- <cookie-config>
- <name>SID</name>
- <max-age>5000</max-age>
- </cookie-config>
- </session-config>
4) 使Session物件立即無效(Web容器會立即銷燬該物件並回收其記憶體空間):void invalidate();
!!呼叫後不得在使用其它函式獲取、修改Session中的資訊,因為其空間已經被回收了,更不能再次invalidate,因為此時引用已經和物件脫鉤,如果發生以上任何一種行為都會引發指標異常並丟擲IllegalStateException異常;
!!這就相當於C++中手動呼叫解構函式;