- 論壇徽章:
- 0
|
關(guān)于HttpSession的誤解實在是太多了,本來是一個很簡單的問題,怎會搞的如此的復雜呢?下面說說我的理解吧:
一個session就是一系列某用戶和服務器間的通訊。服務器有能力分辨出不同的用戶。一個session的建立是從一個用戶向服務器發(fā)第一個請求開始,而以用戶顯式結(jié)束或session超時為結(jié)束。
其工作原理是這樣的:
1.當一個用戶向服務器發(fā)送第一個請求時,服務器為其建立一個session,并為此session創(chuàng)建一個標識號;
2.這個用戶隨后的所有請求都應包括這個標識號。服務器會校對這個標識號以判斷請求屬于哪個session。
這種機制不使用IP作為標識,是因為很多機器是通過代理服務器方式上網(wǎng),沒法區(qū)分每一臺機器。
對于session標識號(sessionID),有兩種方式實現(xiàn):cookies和URL重寫。
HttpSession的使用
我們來看看在API中對session是如何定義和操作的。
當需要為用戶端建立一個session時,servlet容器就創(chuàng)建了一個HttpSession對象。其中存儲了和本session相關(guān)的信息。所以,在一個servlet中有多少個不同用戶連接,就會有多少個HttpSession對象。
使用的機理是:
1.從請求中提取HttpSession對象;
2.增加或刪除HttpSession中的屬性;
3.根據(jù)需要關(guān)閉HttpSession或使其失效。
在請求中有兩個重載的方法用來獲取HttpSession對象。
HttpSession getSession(boolean create)/getSession();作用是提取HttpSession對象,如果沒有自動創(chuàng)建。
獲取到HttpSession對象后,我們就需要使用HttpSession的某些方法去設置和更改某些參數(shù)了。如:
void setAttribute(String name, Object value);
Object getAttribute(String name);
void removeAttribute(String name);
在javax.servlet.http包里一共定義了四個session監(jiān)聽器接口和與之關(guān)聯(lián)的兩個session事件。分別是:
HttpSessionAttributeListener and HttpSessionBindingEvent;
HttpSessionBindingListener and HttpSessionBindingEvent;
HttpSessionListener and HttpSessionEvent;
HttpSessionActivationListener and HttpSessionEvent.
他們的繼承關(guān)系是:
所有四個接口的父類是java.util.EventListener;
HttpSessionEvent擴展java.util.EventObject;
而HttpSessionBindingEvent又擴展了HttpSessionEvent。
以下分別詳述:
HttpSessionAttributeListener
當session中的屬性被添加,更改,刪除時得到通知。這個接口上節(jié)講過,主要看其它三個。
HttpSessionBindingListener
當一個實現(xiàn)了HttpSessionBindingListener的類被加入到HttpSession中(或從中移出)時,會產(chǎn)生HttpBindingEvent事件,而這些事件會被它本身接收到。
本接口定義了兩個方法:
void valueBound(HttpSessionBindingEvent e);
void valueUnbound(HttpSessionBindingEvent e);
當多個實現(xiàn)了HttpSessionBindingListener的類被加入到HttpSession中時,各類的方法只對本類感興趣,不會去理會其它類的加入。
即使是同一類的不同實例間,也是互不關(guān)心的(各掃門前雪)。
我們可以看到前兩個接口都對HttpSessionBindingEvent事件做出反應,但機理不同。
HttpSessionAttributeListener是在web.xml中登記的,servlet容器僅創(chuàng)建一個實例,來為任何在session中增加屬性的servlet服務。觸發(fā)事件的對象是所有可以轉(zhuǎn)換為Object的實例。
HttpSessionBindingListener不用在web.xml中登記,在每個servlet中用new創(chuàng)建實例,且僅對本實例向session中的加入(或移出)感興趣。觸發(fā)事件的對象僅僅是自己。
HttpSessionListener
對于session的創(chuàng)建和取消感興趣。需要在web.xml中登記。
共有兩個方法:
void sessionCreated(HttpSessionEvent se);
void sessionDestroyed(HttpSessionEvent se);
使用它我們可以容易的創(chuàng)建一個類來對session計數(shù)。
也許我們會簡單的考慮使用sessionDestroyed方法來在session結(jié)束后做一些清理工作。但是,請注意,當這個方法被調(diào)用的時候,session已經(jīng)結(jié)束了,你不能從中提取到任何信息了。因此,我們要另辟蹊徑。
一種通常采用的方法是使用HttpSessionBindingListener接口。在session創(chuàng)建時增加一個屬性,而在session結(jié)束前最后一件事將這個屬性刪除,這樣就會觸發(fā)valueUnbound方法,所有對session的清理工作可以在這個方法中實現(xiàn)。
HttpSessionActivationListener
當session在分布式環(huán)境中跨JVM時,實現(xiàn)該接口的對象得到通知。共兩個方法:
void sessionDidActivate(HttpSessionEvent se);
void sessionWillPassivate(HttpSessionEvent se);
1、HTTP協(xié)議本身是“連接-請求-應答-關(guān)閉連接”模式的,是一種無狀態(tài)協(xié)議(HTTP只是一個傳輸協(xié)議);
2、Cookie規(guī)范是為了給HTTP增加狀態(tài)跟蹤用的(如果要精確把握,建議仔細閱讀一下相關(guān)的RFC),但不是唯一的手段;
3、所謂Session,指的是客戶端和服務端之間的一段交互過程的狀態(tài)信息(數(shù)據(jù));這個狀態(tài)如何界定,生命期有多長,這是應用本身的事情;
4、由于B/S計算模型中計算是在服務器端完成的,客戶端只有簡單的顯示邏輯,所以,Session數(shù)據(jù)對客戶端應該是透明的不可理解的并且應該受控于服務端;Session數(shù)據(jù)要么保存到服務端(HttpSession),要么在客戶端和服務端之間傳遞(Cookie或url rewritting或Hidden input);
5、由于HTTP本身的無狀態(tài)性,服務端無法知道客戶端相繼發(fā)來的請求是來自一個客戶的,所以,當使用服務端HttpSession存儲會話數(shù)據(jù)的時候客戶端的每個請求都應該包含一個session的標識(sid, jsessionid 等等)來告訴服務端;
6、會話數(shù)據(jù)保存在服務端(如HttpSession)的好處是減少了HTTP請求的長度,提高了網(wǎng)絡傳輸效率;客戶端session信息存儲則相反;
7、客戶端Session存儲只有一個辦法:cookie(url rewritting和hidden input因為無法做到持久化,不算,只能作為交換session id的方式,即a method of session tracking),而服務端做法大致也是一個道理:容器有個session管理器(如tomcat的org.apache.catalina.session包里面的類),提供session的生命周期和持久化管理并提供訪問session數(shù)據(jù)的api;
8、使用服務端還是客戶端session存儲要看應用的實際情況的。一般來說不要求用戶注冊登錄的公共服務系統(tǒng)(如google)采用cookie做客戶端session存儲(如google的用戶偏好設置),而有用戶管理的系統(tǒng)則使用服務端存儲。原因很顯然:無需用戶登錄的系統(tǒng)唯一能夠標識用戶的就是用戶的電腦,換一臺機器就不知道誰是誰了,服務端session存儲根本不管用;而有用戶管理的系統(tǒng)則可以通過用戶id來管理用戶個人數(shù)據(jù),從而提供任意復雜的個性化服務;
9、客戶端和服務端的session存儲在性能、安全性、跨站能力、編程方便性等方面都有一定的區(qū)別,而且優(yōu)劣并非絕對(譬如TheServerSide號稱不使用HttpSession,所以性能好,這很顯然:一個具有上億的訪問用戶的系統(tǒng),要在服務端數(shù)據(jù)庫中檢索出用戶的偏好信息顯然是低效的,Session管理器不管用什么數(shù)據(jù)結(jié)構(gòu)和算法都要耗費大量內(nèi)存和CPU時間;而用cookie,則根本不用檢索和維護session數(shù)據(jù),服務器可以做成無狀態(tài)的,當然高效);
10、所謂的“會話cookie”簡單的說就是沒有明確指明有效期的cookie,僅在瀏覽器當前進程生命期內(nèi)有效,可以被后繼的Set-Cookie操作清除掉。
當程序需要為某個客戶端的請求創(chuàng)建一個session的時候,服務器首先檢查這個客戶端的請求里是否已包含了一個session標識 - 稱為 session id,如果已包含一個session id則說明以前已經(jīng)為此客戶端創(chuàng)建過session,服務器就按照session id把這個 session檢索出來使用(如果檢索不到,可能會新建一個),如果客戶端請求不包含session id,則為此客戶端創(chuàng)建一個session并且生成一個與此session相關(guān)聯(lián)的session id,session id的值應該是一個既不會重復,又不容易被找到規(guī)律以仿造的字符串,這個 session id將被在本次響應中返回給客戶端保存。
保存這個session id的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規(guī)則把這個標識發(fā)揮給服務器。一般這個cookie的名字都是類似于SEEESIONID.
1、session在何時被創(chuàng)建
一個常見的誤解是以為session在有客戶端訪問時就被創(chuàng)建,然而事實是直到某server端程序調(diào)用 HttpServletRequest.getSession(true)這樣的語句時才被創(chuàng)建,注意如果JSP沒有顯示的使用 關(guān)閉session,則JSP文件在編譯成Servlet時將會自動加上這樣一條語句 HttpSession session = HttpServletRequest.getSession(true);這也是JSP中隱含的 session對象的來歷。由于session會消耗內(nèi)存資源,因此,如果不打算使用session,應該在所有的JSP中關(guān)閉它。
2、存放在session中的對象必須是可序列化的嗎
不是必需的。要求對象可序列化只是為了session能夠在集群中被復制或者能夠持久保存或者在必要時server能夠暫時把session交換出內(nèi)存。
3、如何才能正確的應付客戶端禁止cookie的可能性
對所有的URL使用URL重寫,包括超鏈接,form的action,和重定向的URL,具體做法參見:
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
Instead, use the HttpServletResponse.encodeURL() method, for example:
out.println("catalog");
Calling the encodeURL() method determines if the URL needs to be rewritten, and if so, it rewrites it by including the session ID in the URL. The session ID is appended to the URL and begins with a semicolon.
In addition to URLs that are returned as a response to WebLogic Server, also encode URLs that send redirects. For example:
if (session.isNew()) response.sendRedirect (response.encodeRedirectUrl(welcomeURL));
4、開兩個瀏覽器窗口訪問應用程序會使用同一個session還是不同的session
參見第三小節(jié)對cookie的討論,對session來說是只認id不認人,因此不同的瀏覽器,不同的窗口打開方式以及不同的cookie存儲方式都會對這個問題的答案有影響。
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u3/109937/showart_2156640.html |
|