HttpSession的正確理解
關于HttpSession的誤解實在是太多了,本來是一個很簡單的問題,怎會搞的如此的復雜呢?下面說說我的理解吧:一個session就是一系列某用戶和服務器間的通訊。服務器有能力分辨出不同的用戶。一個session的建立是從一個用戶向服務器發第一個請求開始,而以用戶顯式結束或session超時為結束。
其工作原理是這樣的:
1.當一個用戶向服務器發送第一個請求時,服務器為其建立一個session,并為此session創建一個標識號;
2.這個用戶隨后的所有請求都應包括這個標識號。服務器會校對這個標識號以判斷請求屬于哪個session。
這種機制不使用IP作為標識,是因為很多機器是通過代理服務器方式上網,沒法區分每一臺機器。
對于session標識號(sessionID),有兩種方式實現:cookies和URL重寫。
HttpSession的使用
我們來看看在API中對session是如何定義和操作的。
當需要為用戶端建立一個session時,servlet容器就創建了一個HttpSession對象。其中存儲了和本session相關的信息。所以,在一個servlet中有多少個不同用戶連接,就會有多少個HttpSession對象。
使用的機理是:
1.從請求中提取HttpSession對象;
2.增加或刪除HttpSession中的屬性;
3.根據需要關閉HttpSession或使其失效。
在請求中有兩個重載的方法用來獲取HttpSession對象。
HttpSession getSession(boolean create)/getSession();作用是提取HttpSession對象,如果沒有自動創建。
獲取到HttpSession對象后,我們就需要使用HttpSession的某些方法去設置和更改某些參數了。如:
void setAttribute(String name, Object value);
Object getAttribute(String name);
void removeAttribute(String name);
在javax.servlet.http包里一共定義了四個session監聽器接口和與之關聯的兩個session事件。分別是:
HttpSessionAttributeListener and HttpSessionBindingEvent;
HttpSessionBindingListener and HttpSessionBindingEvent;
HttpSessionListener and HttpSessionEvent;
HttpSessionActivationListener and HttpSessionEvent.
他們的繼承關系是:
所有四個接口的父類是java.util.EventListener;
HttpSessionEvent擴展java.util.EventObject;
而HttpSessionBindingEvent又擴展了HttpSessionEvent。
以下分別詳述:
HttpSessionAttributeListener
當session中的屬性被添加,更改,刪除時得到通知。這個接口上節講過,主要看其它三個。
HttpSessionBindingListener
當一個實現了HttpSessionBindingListener的類被加入到HttpSession中(或從中移出)時,會產生HttpBindingEvent事件,而這些事件會被它本身接收到。
本接口定義了兩個方法:
void valueBound(HttpSessionBindingEvent e);
void valueUnbound(HttpSessionBindingEvent e);
當多個實現了HttpSessionBindingListener的類被加入到HttpSession中時,各類的方法只對本類感興趣,不會去理會其它類的加入。
即使是同一類的不同實例間,也是互不關心的(各掃門前雪)。
我們可以看到前兩個接口都對HttpSessionBindingEvent事件做出反應,但機理不同。
HttpSessionAttributeListener是在web.xml中登記的,servlet容器僅創建一個實例,來為任何在session中增加屬性的servlet服務。觸發事件的對象是所有可以轉換為Object的實例。
HttpSessionBindingListener不用在web.xml中登記,在每個servlet中用new創建實例,且僅對本實例向session中的加入(或移出)感興趣。觸發事件的對象僅僅是自己。
HttpSessionListener
對于session的創建和取消感興趣。需要在web.xml中登記。
共有兩個方法:
void sessionCreated(HttpSessionEvent se);
void sessionDestroyed(HttpSessionEvent se);
使用它我們可以容易的創建一個類來對session計數。
也許我們會簡單的考慮使用sessionDestroyed方法來在session結束后做一些清理工作。但是,請注意,當這個方法被調用的時候,session已經結束了,你不能從中提取到任何信息了。因此,我們要另辟蹊徑。
一種通常采用的方法是使用HttpSessionBindingListener接口。在session創建時增加一個屬性,而在session結束前最后一件事將這個屬性刪除,這樣就會觸發valueUnbound方法,所有對session的清理工作可以在這個方法中實現。
HttpSessionActivationListener
當session在分布式環境中跨JVM時,實現該接口的對象得到通知。共兩個方法:
void sessionDidActivate(HttpSessionEvent se);
void sessionWillPassivate(HttpSessionEvent se);
1、HTTP協議本身是“連接-請求-應答-關閉連接”模式的,是一種無狀態協議(HTTP只是一個傳輸協議);
2、Cookie規范是為了給HTTP增加狀態跟蹤用的(如果要精確把握,建議仔細閱讀一下相關的RFC),但不是唯一的手段;
3、所謂Session,指的是客戶端和服務端之間的一段交互過程的狀態信息(數據);這個狀態如何界定,生命期有多長,這是應用本身的事情;
4、由于B/S計算模型中計算是在服務器端完成的,客戶端只有簡單的顯示邏輯,所以,Session數據對客戶端應該是透明的不可理解的并且應該受控于服務端;Session數據要么保存到服務端(HttpSession),要么在客戶端和服務端之間傳遞(Cookie或url rewritting或Hidden input);
5、由于HTTP本身的無狀態性,服務端無法知道客戶端相繼發來的請求是來自一個客戶的,所以,當使用服務端HttpSession存儲會話數據的時候客戶端的每個請求都應該包含一個session的標識(sid, jsessionid 等等)來告訴服務端;
6、會話數據保存在服務端(如HttpSession)的好處是減少了HTTP請求的長度,提高了網絡傳輸效率;客戶端session信息存儲則相反;
7、客戶端Session存儲只有一個辦法:cookie(url rewritting和hidden input因為無法做到持久化,不算,只能作為交換session id的方式,即a method of session tracking),而服務端做法大致也是一個道理:容器有個session管理器(如tomcat的org.apache.catalina.session包里面的類),提供session的生命周期和持久化管理并提供訪問session數據的api;
8、使用服務端還是客戶端session存儲要看應用的實際情況的。一般來說不要求用戶注冊登錄的公共服務系統(如google)采用cookie做客戶端session存儲(如google的用戶偏好設置),而有用戶管理的系統則使用服務端存儲。原因很顯然:無需用戶登錄的系統唯一能夠標識用戶的就是用戶的電腦,換一臺機器就不知道誰是誰了,服務端session存儲根本不管用;而有用戶管理的系統則可以通過用戶id來管理用戶個人數據,從而提供任意復雜的個性化服務;
9、客戶端和服務端的session存儲在性能、安全性、跨站能力、編程方便性等方面都有一定的區別,而且優劣并非絕對(譬如TheServerSide號稱不使用HttpSession,所以性能好,這很顯然:一個具有上億的訪問用戶的系統,要在服務端數據庫中檢索出用戶的偏好信息顯然是低效的,Session管理器不管用什么數據結構和算法都要耗費大量內存和CPU時間;而用cookie,則根本不用檢索和維護session數據,服務器可以做成無狀態的,當然高效);
10、所謂的“會話cookie”簡單的說就是沒有明確指明有效期的cookie,僅在瀏覽器當前進程生命期內有效,可以被后繼的Set-Cookie操作清除掉