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