來源:http://blog.csdn.net/mydream83/archive/2007/02/18/1511639.aspx
來公司兩個月了, 進組也有1個多月,這是我的第一份工作, 也是第一個項目, 所以決定留下文字記錄還是好些,由於入組相對晚,我負責的也相對獨立些 -- 系統管理, 首先要做的就是登入, 既然是應用系統肯定不允許同一帳號異地重複登入, 就這個問題我查了很多資料, 想做的完美些, 可謂是聽取了百家之言。
我目前所知的方法可分為兩種, 一是資料庫來記錄使用者線上情況, 在登入把該使用者資料庫的識別欄位賦值, 退出時讓該欄位複位. 二是通過全域的記憶體對象來記錄使用者情況, 以該使用者的登入名稱如user做為key存入application中或是定義的一個全域的hashmap中, 退出刪除全域對象中該user對應的資訊, 據說還可用Cache全域對象,但相比.NET,java實現Cache不是太容易. 而這些對於使用者的登入事件都能即時的做出處理, 但使用者的退出顯然多種多樣, 乖乖點擊登出可能性很小, 還得考慮斷電,掉線等情況, 所以使用者的退出事件程式是捕獲不了的, 這是由session的機制決定的, 所以使用者退出時,伺服器想做到即時重新整理是幾乎是不可能的(除非使用者點登出), 很多線上統計人數都不是即時的, 一般都是通過添加session監聽器, 然後設定session失效時間, 時間到了則調用監聽器的對應方法釋放相關的資料, 如刪除application中該使用者的資訊. 所以很可能會出現的一種情況是使用者剛剛登入上系統, 然後掉線或其它原因, 然後該使用者重新登入, 但系統提示該使用者已經登入, 無法再次登入. 只能等到原來的session失效, 遇到這種情況想必是很鬱悶. 這種情況是無法解決的, 就其根本還是因為使用者的退出是無法確定的, 伺服器無法區分使用者的真線上還是假線上. 既然這樣可以像QQ一樣以頂號的方式來處理, 就是使用者登入時判斷application中是否有該使用者資訊存在, 有則替換, 無則寫入, 這樣同一個帳號的話, 先登入的會被後登入的踢出, 這就無需理會先登入的使用者是真線上還是假線上, 達到了即時的效果。不過我們項目不讓這麼用, 目辦法只能用session監聽了。Servlet裡面一共有八個監聽, 其中有四個是session的監聽介面與之關聯的兩個session事件,
這四個介面分別是:
HttpSessionAttributeListener and HttpSessionBindingEvent
HttpSessionBindingListener and HttpSessionBindingEvent
HttpSessionListener and HttpSessionEvent
HttpSessionActivationListener and HttpSessionEvent
HttpSessionAttributeListener: 用來監聽session中的屬性添加, 替換和刪除, 只需建立一個類來實現HttpSessionAttributeListener介面, 然後在web.xml配個listener屬性,其中litener-class是這個類的路徑. 該類會實現HttpSessionAttributeListener的三個方法. 分別對應session中的屬性添加, 替換和刪除. 該類無需我們執行個體化使用, 因為servlet容器會建立該類的一個執行個體, 並是單例. 所以訪問該系統的所有使用者對session屬性的操作都會被HttpSessionAttributeListener監聽到. HttpSessionBindingEvent事件的觸發對象是任何object的執行個體, 因為session的setAttribute方法中可以存放任何object的執行個體。HttpSessionBindingListener: 建立實現HttpSessionBindingListener介面的類, 不需在web.xml中配置, 該類會實現HttpSessionBindingListener的兩個方法valueBound()和valueUnbound(), 該類需要執行個體化使用, 將該類執行個體對象放入session後會馬上觸發valueBound()方法, 從session中remove掉該執行個體時或session逾時會馬上觸發valueUnbound(), 很顯然每個servlet都會建立一個該類的對象,所以多使用者並發調用時它們都是相互獨立的, 而且可以建立多個實現HttpSessionBindingListener介面的類, 效果依然一樣。
HttpSessionBindingEvent事件的觸發對象是該類的執行個體本身(有點繞口),
舉例
public class SessionListener implements HttpSessionBindingListener....
SessionListener sessionListener = new SessionListener();
//下面這句即是觸發HttpSessionBindingEvent事件, 而觸發對象正是sessionListener, 監聽器的執行個體本身
request.getSession().setAttribute(request.getSession().getId(), sessionListener);
HttpSessionListener:監聽session的建立和釋放, 需在web.xml中配置, 重寫sessionCreated()和sessionDestroyed()方法, 它同樣是容器建立的單列對象, 可以監聽所有使用者的session建立, 適合來做統計, 得注意的一點是, 在瀏覽器訪問伺服器時, session的會話就建立了, 而session釋放後才會調用sessionDestroyed()方法, 所以不能在sessionDestroyed()內做session清理工作, 因為已經無法獲得session了。
HttpSessionActivationListener:當session在分布式環境中跨JVM時,實現該介面的對象得到通知, 需實行的方法sessionDidActivate()和sessionWillPassivate(), Activate與Passivate是用於置換對象的動作,當session對象為了資源利用或Server Load Balancer等原因而必須暫時儲存至硬碟或其它儲存器時(透過對象序列化),所作的動作稱之為Passivate,而硬碟或儲存器上的session對象重新載入JVM時所採的動作稱之為Activate,所以容易理解的,sessionDidActivate()與 sessionWillPassivate()分別於Activeate後與將Passivate前呼叫。
這四個都與session有關,但能做到驗證登入的只有HttpSessionBindingListener and HttpSessionListener,無疑使用前者更合適些。