javascript
Head First JSP---随笔四
會話狀態
Web服務器沒有短期記憶。在Servlet API中可以找到一種極其簡單的解決方法。
會話管理
4.1 編寫servlet代碼,將對象保存到一個會話對象中,以及從會話對象獲得對象。
4.2 給定一個場景,描述訪問會話對象使用的API,解釋何時創建會話對象,并描述撤銷會話對象使用的機制,以及何時撤銷會話對象。
4.3 使用會話監聽者,編寫代碼對會話的有關時間做出響應,包括向會話增加一個對象,以及會話對象從VM遷移到另一個VM。
4.4 給定一個場景,說明Web容器可以采用哪些會話管理機制,如何使用cookie來管理會話,以及如何使用URL重寫管理會話,并編寫servlet代碼完成URL重寫。
容器怎么知道客戶是誰?
對容器而言,每個請求都來自于一個新的客戶。
解決:客戶需要一個唯一的會話ID。容器會生成一個唯一的會話ID,并通過響應把它返回給客戶。客戶在以后的每一個請求中發回這個會話ID給服務器。如下圖:
容器機會做cookie的所有工作
在響應中發送一個會話cookie與從請求得到會話ID的代碼一樣,如下:
HttpSession session = request.getSession();就這么簡單。在你的服務方法中請求一個會話,余下的所有事情都會自動完成。
自動完成的事:
- 建立新的HttpSession
- 生成唯一的會話ID
- 建立新的cookie對象
- 會話ID與cookie關聯
- 響應中設置cookie(set-cookie首部下)
- cookie的所有工作都在后臺進行
怎么知道會話已經存在,還是剛剛創建?
請求的無參方法getSession會返回一個會話,而不論是否已經有一個會話。因為我們總能從這個方法得到返回的一個HttpSession實例。所以,要知道會話是不是新創建的,只有去問會話。
HttpSession session = request.getSession();if(session.isNew()){//客戶還沒用過這個會話,或者說是新創建的 }else{//客戶已經使用過這個會話 }這里指出:得到會話的方式不止request.getSession()一種。
如果我只想要一個已經有的會話呢?
某些情況下,servlet可能只想使用一個原來創建的會話。例如,讓結賬servlet再開始一個新的會話可能就不太合適。
所以,針對這個目的,專門有一個重載的getSession(boolean)方法。
//傳遞一個false表示,這個方法會返回一個已經有的會話,如果沒有與此客戶關聯的會話,則會返回null HttpSession session = request.getSession(false);if(session==null){//會話不存在,創建一個session = request.getSession(); }else{//會話以存在 }比較上面2種獲取session的方法,它們其實是一樣的。除非你并不想創建一個session就可以直接用getSession(false),當不存在一個session的時候,你判斷為空,并不去創建一個新的session。
如果不能用cookie
如果客戶禁用掉cookie,那么客戶就會忽略“Set-Cookie”響應首部。這樣的話,那又該如何處理呢?(session的機制就是靠cookie實現的,所以cookie不能用就等于session不能用)
就算客戶不接受cookie,我們也能完成會話,但是必須稍微多做點工作。
解決:URL重寫(一條后路),如下:
URL+;jsessionid=1234567
如圖:
還差最后一步:需要告訴響應要對URL編碼,URL重寫才能奏效。
PrintWriter out = response.getWriter(); HttpSession session = request.getSession();//向這個"/BeerTest.do"這個URL增加額外的會話ID信息 out.println(response.encodeURL("/BeerTest.do"));這里還有一個問題,容器怎么知道客戶端cookie不能正常工作?
方法很簡單:返回第一個響應時,它會同時嘗試cookie和URL重寫這兩種方式。
使用sendRedirect()的URL重寫
可能會有這種情況,你想把請求重定向到另外一個URL,但是還想使用一個會話。為此,有一個特殊的URL編碼方法:response.encodeRedirectURL("/BeerTest.do")。
會話注意點
URL重寫要點
刪除會話
由于客戶可能會因為各種原因導致會話結束,可是我們沒將Session銷毀,這個時候容器又如何知道什么時候能安全地撤銷一個會話?
HttpSession接口的UML
| getCreationTime() | 返回第一次創建會話的時間 | 得出這個會話有多“老”。你可能想要把某些會話的壽命限制為一個固定的值。例如,你可能會說“一旦登錄,就必須在10分鐘之內完成這個表單” |
| getLastAccesssedTime() | 返回容器最后一次得到包含這個會話ID的請求后過去了多長時間(毫秒數) | 得出客戶最后一次訪問這個會話是什么時候。可以用這個方法來確定客戶是否已經離開很長時間,這樣就可以像客戶發出一份email,詢問他們是否還回來。或者可以調用invalidate()結束會話 |
| setMaxInactiveInterval() | 指定對于這個會話客戶請求的最大間隔時間(秒數) | 如果已經過去了指定的時間,而客戶未對這個會話做任何請求,就會導致會話被撤銷。可以用這個方法減少服務器中無用的會話 |
| getMaxInactiveInterval() | 返回對于這個會話客戶請求的最大時間間隔 | 得出這個會話可以保持多長時間不活動當仍“存活”。可以使用這個方法來判斷一個會話不活動的客戶在會話撤銷之前還有多長的“壽命” |
| invalidate() | 結束會話。當前存儲在這個會話中的所有會話屬性也會解除綁定 | 如果客戶已經不活動,或者你知道會話已經結束(例如,客戶完成了購物結賬,或已經注銷),可以用這個方法殺死(撤銷)會話。會話實例本身可能會被容器回收,但是這一點我們并不關心。置無效(Invaldate)意味著會話ID不再存在,而且屬性會從會話對象刪除 |
由UML提供給我們的信息,我們知道Session銷毀可以通過設置時長,和手動刪除(invalidate())。
還有一種死法:應用結束(奔潰或取消部署)
(1)在DD中配置會話超時
<web-app ...><session-config><session-timeout>15</session-time></session-config> </web-app>(2)設置一個特定會話的會話超時
session.setMaxInactiveInterval(20*60); //表示20分鐘生存時間cookie可以另作其他用處嗎?cookie是不是只能用于會話?
可以使用cookie在服務器和客戶之間交換名/值String對。
服務器把cookie發送給客戶,客戶再在以后的每個請求中發回這個cookie。
客戶的瀏覽器退出時,會話cookie就會消失,但是你可以告訴cookie在客戶端上待得更久一些,甚至在瀏覽器關閉之后還持久保存。
利用servlet API使用cookie
代碼如下:
cookie注意點:
HttpSession的重要里程碑
會話生命周期時間
| 生命周期:創建(此時會話是新的)會話、撤銷(invalidate方法)會話 | HttpSessionEvent、HttpSessionListener |
| 屬性:增加、刪除、替換一個屬性 | HttpSessionBindingEvent、HttpSessionAttributeListener |
| 遷移:會話準備鈍化、會話已經激活 | HttpSessionEvent、HttpSessionActivationListener |
HttpSessionBindingListener實例:
import javax.servlet.http.*; public class Dog implements HttpSessionBindingListener {private String breed;public Dog(String breed){this.breed = breed;}public String getBreed(){return breed;}public void valueBound(HttpSessionBindingEvent event){//我知道我在一個會話中時要運行的代碼}public void valueUnbound(HttpSessionBindingEvent event){//我知道已經不在一個會話中時要運行的代碼}}啤酒Web應用分布在兩個VM上
HttpSessionActivationListener:
會話遷移和串行化。
容器需要遷移Serializable屬性(類的屬性要么為null要么是Serializable)。
但是容器不一定非要使用串行化來遷移HttpSession對象(屬性類是Serializable)。
這里指出:tomcat會把輸出(System.out.println())放在tomcat的logs/catalina.log文件中。
監聽者例子:
會話計數器:這個監聽者允許你跟蹤這個Web應用中活動會話的個數。
public class BeerSessionCounter implements HttpSessionListener {private static int activeSession;public static int getActiveSession(){return activeSession;}public void sessionCreated(HttpSessionEvent event){activeSession++;}public void sessionDestroyed(HttpSessionEvent event){activeSession--;}}屬性監聽者:利用這個監聽者,每一次向會話增加屬性、刪除屬性或替換屬性時你都能跟蹤到。
public class BeerAttributeListener implements HttpSessionAttributeListener {public void attributeAdded(HttpSessionBindingEvent event){String name = event.getName();Object value = event.getValue();System.out.println("Attribute added: " + name + ": " + value)}public void attributeRemove(HttpSessionBindingEvent event){String name = event.getName();Object value = event.getValue();System.out.println("Attribute removed: " + name + ": " + value)}public void attributeReplaced(HttpSessionBindingEvent event){String name = event.getName();Object value = event.getValue();System.out.println("Attribute replaced: " + name + ": " + value)} }屬性類的監聽者:這個監聽者允許屬性跟蹤可能對屬性本身很重要事情,如增加到會話,或從會話刪除,已經會話從一個VM遷移到另一個VM。
public class Dog implements HttpSessionBindingListener,HttpSessionActivationListener,Serializable {private String breed;//假設這里還有更多實例變量,包括一些非Serializable的實例變量//假設這里是構造函數已經其他get/set方法public void valueBound(HttpSessionBindingEvent event){//我知道我在一個會話中時要運行的代碼}public void valueUnbound(HttpSessionBindingEvent event){//我知道已經不在一個會話中時要運行的代碼}public void sessionWillPassivate(HttpSessionEvent event){//這些代碼將非Serializable字段置為某種狀態,以便順利地遷移到一個新VM}public void sessionDidActivate(HttpSessionEvent event){//這些代碼用于回復字段...取消在sessionWillPassivate()中做的動作}}DD文件配置
<web-app ...><listener><listener-class>****.*Listener</listener-class></listener> </web-app>與會話相關的監聽者
| 你想知道有多少個并發用戶。也就是說,你想跟蹤活動的會話 | HttpSessionListener(javax.servlet.http) sessionCreated、sessionDestroyed | HttpSessionEvent | 其他類 |
| 你想知道會話何時從一個VM移到另一個VM | HttpSessionActivateListener(javax.servlet.http) sessionDidActivate、sessionWillPassivate | HttpSessionEvent | 屬性類、其他類 |
| 有一個屬性類(這個類的對象要用作為一個屬性值),而且你希望此類對象綁定到會話或從會話刪除時得到通知 | HttpSessionBindingListener(javax.servlet.http) valueBound、valueUnbound | HttpSessionBindingEvent | 屬性類 |
| 你想知道會話中什么時候增加、刪除、替換會話屬性 | HttpSessionAttributeListener(javax.servlet.http) attributeAdded、attributeRemoved、attributeReplaced | HttpSessionBindingEvent | 其他類 |
監聽者的UML圖
本章完
總結
以上是生活随笔為你收集整理的Head First JSP---随笔四的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国游戏行业观察报告
- 下一篇: 数字社交圈里的白酒“新”消费——腾讯20