HttpSession常見問題 – JAVA編程語言程序開發技術文章

1、session在何時被創建 

    一個常見的誤解是以為session在有客戶端訪問時就被創建,然而事實是直到某server端程序調用 HttpServletRequest.getSession(true)這樣的語句時才被創建,註意如果JSP沒有顯示的使用 <%@page session=”false”%> 關閉session,則JSP文件在編譯成Servlet時將會自動加上這樣一條語句HttpSession session = HttpServletRequest.getSession(true);這也是JSP中隱含的session對象的來歷。 

    由於session會消耗內存資源,因此,如果不打算使用session,應該在所有的JSP中關閉它。 

    2、session何時被刪除 

    綜合前面的討論,session在下列情況下被刪除a.程序調用HttpSession.invalidate();或b.距離上一次收到客戶端發送的session id時間間隔超過瞭session的超時設置;或c.服務器進程被停止(非持久session) 

    3、如何做到在瀏覽器關閉時刪除session 

    嚴格的講,做不到這一點。可以做一點努力的辦法是在所有的客戶端頁面裡使用javascript代碼window.oncolose來監視瀏覽器的關閉動作,然後向服務器發送一個請求來刪除session。但是對於瀏覽器崩潰或者強行殺死進程這些非常規手段仍然無能為力。 

    4、有個HttpSessionListener是怎麼回事 

    你可以創建這樣的listener去監控session的創建和銷毀事件,使得在發生這樣的事件時你可以做一些相應的工作。註意是session的創建和銷毀動作觸發listener,而不是相反。類似的與HttpSession有關的listener還有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。 

    5、存放在session中的對象必須是可序列化的嗎 

    不是必需的。要求對象可序列化隻是為瞭session能夠在集群中被復制或者能夠持久保存或者在必要時server能夠暫時把session交換出內存。在 Weblogic Server的session中放置一個不可序列化的對象在控制臺上會收到一個警告。我所用過的某個iPlanet版本如果session中有不可序列化的對象,在session銷毀時會有一個Exception,很奇怪。 

    6、如何才能正確的應付客戶端禁止cookie的可能性 

    對所有的URL使用URL重寫,包括超鏈接,form的action,和重定向的URL,具體做法參見[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770 

    7、開兩個瀏覽器窗口訪問應用程序會使用同一個session還是不同的session 

    參見第三小節對cookie的討論,對session來說是隻認id不認人,因此不同的瀏覽器,不同的窗口打開方式以及不同的cookie存儲方式都會對這個問題的答案有影響。 

    8、如何防止用戶打開兩個瀏覽器窗口操作導致的session混亂 

    這個問題與防止表單多次提交是類似的,可以通過設置客戶端的令牌來解決。就是在服務器每次生成一個不同的id返回給客戶端,同時保存在session裡,客戶端提交表單時必須把這個id也返回服務器,程序首先比較返回的id與保存在session裡的值是否一致,如果不一致則說明本次操作已經被提交過瞭。可以參看《J2EE核心模式》關於表示層模式的部分。需要註意的是對於使用javascript window.open打開的窗口,一般不設置這個id,或者使用單獨的id,以防主窗口無法操作,建議不要再window.open打開的窗口裡做修改操作,這樣就可以不用設置。 

    9、為什麼在Weblogic Server中改變session的值後要重新調用一次session.setValue 
    
    做這個動作主要是為瞭在集群環境中提示Weblogic Server session中的值發生瞭改變,需要向其他服務器進程復制新的session值。 

    10、為什麼session不見瞭 

    排除session正常失效的因素之外,服務器本身的可能性應該是微乎其微的,雖然筆者在iPlanet6SP1加若幹補丁的Solaris版本上倒也遇到過;瀏覽器插件的可能性次之,筆者也遇到過3721插件造成的問題;理論上防火墻或者代理服務器在cookie處理上也有可能會出現問題。
出現這一問題的大部分原因都是程序的錯誤,最常見的就是在一個應用程序中去訪問另外一個應用程序。我們在下一節討論這個問題。 

    七、跨應用程序的session共享 

    常常有這樣的情況,一個大項目被分割成若幹小項目開發,為瞭能夠互不幹擾,要求每個小項目作為一個單獨的web應用程序開發,可是到瞭最後突然發現某幾個小項目之間需要共享一些信息,或者想使用session來實現SSO(single sign on),在session中保存login的用戶信息,最自然的要求是應用程序間能夠訪問彼此的session。 

    然而按照Servlet規范,session的作用范圍應該僅僅限於當前應用程序下,不同的應用程序之間是不能夠互相訪問對方的session的。各個應用服務器從實際效果上都遵守瞭這一規范,但是實現的細節卻可能各有不同,因此解決跨應用程序session共享的方法也各不相同。 

    首先來看一下Tomcat是如何實現web應用程序之間session的隔離的,從Tomcat設置的cookie路徑來看,它對不同的應用程序設置的 cookie路徑是不同的,這樣不同的應用程序所用的session id是不同的,因此即使在同一個瀏覽器窗口裡訪問不同的應用程序,發送給服務器的session id也可以是不同的。
 
    
    根據這個特性,我們可以推測Tomcat中session的內存結構大致如下。


    筆者以前用過的iPlanet也采用的是同樣的方式,估計SunONE與iPlanet之間不會有太大的差別。對於這種方式的服務器,解決的思路很簡單,實際實行起來也不難。要麼讓所有的應用程序共享一個session id,要麼讓應用程序能夠獲得其他應用程序的session id。

    iPlanet中有一種很簡單的方法來實現共享一個session id,那就是把各個應用程序的cookie路徑都設為/(實際上應該是/NASApp,對於應用程序來講它的作用相當於根)。

    <session-info>
    <path>/NASApp</path>
    </session-info>

    需要註意的是,操作共享的session應該遵循一些編程約定,比如在session attribute名字的前面加上應用程序的前綴,使得setAttribute(“name”, “neo”)變成setAttribute(“app1.name”, “neo”),以防止命名空間沖突,導致互相覆蓋。

    在Tomcat中則沒有這麼方便的選擇。在Tomcat版本3上,我們還可以有一些手段來共享session。對於版本4以上的Tomcat,目前筆者尚未發現簡單的辦法。隻能借助於第三方的力量,比如使用文件、數據庫、JMS或者客戶端cookie,URL參數或者隱藏字段等手段。

    我們再看一下Weblogic Server是如何處理session的。


    從截屏畫面上可以看到Weblogic Server對所有的應用程序設置的cookie的路徑都是/,這是不是意味著在Weblogic Server中默認的就可以共享session瞭呢?然而一個小實驗即可證明即使不同的應用程序使用的是同一個session,各個應用程序仍然隻能訪問自己所設置的那些屬性。這說明Weblogic Server中的session的內存結構可能如下:

    對於這樣一種結構,在session機制本身上來解決session共享的問題應該是不可能的瞭。除瞭借助於第三方的力量,比如使用文件、數據庫、JMS 或者客戶端cookie,URL參數或者隱藏字段等手段,還有一種較為方便的做法,就是把一個應用程序的session放到ServletContext 中,這樣另外一個應用程序就可以從ServletContext中取得前一個應用程序的引用。示例代碼如下, 

    應用程序A :
    context.setAttribute(“appA”, session); 

    應用程序B :
    contextA = context.getContext(“/appA”); <

發佈留言