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