首先談一下對session對象在web開(kāi)發(fā)中的創(chuàng )建以及sessionId生成并返回客戶(hù)端的運行機制.
session對象當客戶(hù)端首次訪(fǎng)問(wèn)時(shí),創(chuàng )建一個(gè)新的session對象.并同時(shí)生成一個(gè)sessionId,并在此次響應中將sessionId以響應報文的方式些回客戶(hù)端瀏覽器內存或以重寫(xiě)url方式送回客戶(hù)端,來(lái)保持整個(gè)會(huì )話(huà),只要sever端的這個(gè)session對象沒(méi)有銷(xiāo)毀,以后再調用request.getSession() 時(shí)就直接根據客戶(hù)端的sessionId來(lái)檢索server端生成的session對象并返回,不會(huì )再次去新建,除非根據此sessionId沒(méi)有檢索到 session對象.
下面是在IE下測試,因為IE6.0的一個(gè)BUG就是IE的隱私設置即使是阻止所有cookie時(shí),也還是會(huì )以會(huì )話(huà)cookie來(lái)保存sessionId.所以下面都是以會(huì )話(huà)cookie來(lái)討論的,
(1)在server沒(méi)有關(guān)閉,并在session對象銷(xiāo)毀時(shí)間內,當客戶(hù)端再次來(lái)請求server端的servlet或jsp時(shí), 將會(huì )將在第一次請求時(shí)生成的sessionId并附帶在請求信息頭中并向server端發(fā)送,server端收到sessionId后根據此 sessionId會(huì )去搜索(此過(guò)程是透明的)server對應的session對象并直接返回這個(gè)session對象,此時(shí)不會(huì )重新去建立一個(gè)新的 session對象.
(2)當server關(guān)閉(之前產(chǎn)生的session對象也就消亡了),或session對象過(guò)了其銷(xiāo)毀時(shí)間后, 瀏覽器窗口不關(guān),并在本瀏覽器窗口再次去請求sever端的servlet和jsp時(shí),此時(shí)同樣會(huì )將sessionId(server關(guān)閉或 session銷(xiāo)毀時(shí)生成的sessionId)發(fā)送到server端,server根據sessionId去找其對應的session對象,但此時(shí) session對象已經(jīng)不存在,此時(shí)會(huì )重新生成一個(gè)新的session對象,并生成新的sessionId并同樣將這個(gè)新生成的sessionId以響應報文的形式送到瀏覽器內存中.
(3)當server沒(méi)有關(guān)閉,并session對象在其銷(xiāo)毀時(shí)間內,當請求一個(gè)jsp頁(yè)面回客戶(hù)端后, 關(guān)閉此瀏覽器窗口,此時(shí)其內存中的sessionId也就隨之銷(xiāo)毀,在重新去請求sever端的servlet或jsp時(shí),會(huì )重新生成一個(gè) sessionId給客戶(hù)端瀏覽器,并存在瀏覽內存中.
上面的理論在servlet中測試都是成立的,下面談一下在struts框架下進(jìn)行上面的測試時(shí)的不同的地方.
先簡(jiǎn)要說(shuō)下測試程序的流程:
客戶(hù)端請求index.do--->進(jìn)入server端的IndexAction--->轉向login.jsp頁(yè)面----->請求login.do----->進(jìn)入server端的LoginAction.
首先說(shuō)明:IndexAction中沒(méi)有去產(chǎn)生session對象,login.jsp中設置.
(1)環(huán)境servlet + jsp:
在sevlet+jsp測試跟蹤時(shí),在index.do進(jìn)入IndexAction后轉向login.jsp時(shí),此時(shí)瀏覽器內存中是沒(méi)有會(huì )話(huà)cookie的,那么在login.jsp上請求login.do進(jìn)入LoginAction后,用request.getCookies()測試時(shí),其值是為null的!結果是穩合的,因為從始置終沒(méi)有產(chǎn)生過(guò)session嘛!
(2)環(huán)境struts + jsp:
在struts+jsp測試跟蹤時(shí),跟上面的流程一樣,開(kāi)始想結果也應該是一樣的,但經(jīng)過(guò)調試后發(fā)現結果卻不是所想的那樣.在login.do進(jìn)入 LoginActoin后用,用request.getCookies()測試時(shí),發(fā)現其值不為null,即其有name和value,開(kāi)始很不理解,因為根本就沒(méi)有創(chuàng )建過(guò)session對象,哪來(lái)的會(huì )話(huà)cookie值呢.但是結果有,那么想著(zhù)此時(shí)瀏覽器內存中也就應該有會(huì )話(huà)cookie,問(wèn)題就在這里! 從哪里來(lái)的?
后來(lái)經(jīng)過(guò)仔細考慮后,想到struts中的特點(diǎn),我們自己寫(xiě)的Action類(lèi)是繼承struts的Action的,而且之前是經(jīng)過(guò)struts的中央控制器ActionServlet來(lái)控制轉向的,所以我想肯定是在程序進(jìn)入我自己寫(xiě)的IndexAction之前, struts框架中的代碼肯定已經(jīng)創(chuàng )建了session對象并已經(jīng)生成了sessionId.于是就找到相關(guān)書(shū)籍查看了ActionServlet工作流程以及調用哪些類(lèi),看了之后果然在其中看到了HttpSession session = request.getSession();這樣一句話(huà)!于是答案也就明了了.
大家知道struts的ActionServlet類(lèi)中在接收到我們客戶(hù)端的請求(*.do)后(之前會(huì )做一系列初始化工作),并不是直接去處理我們的請求并調用相應的Action(我們寫(xiě)的如 IndexAction),而是將處理工作交給RequestProcessor類(lèi),其process方法中會(huì )調用一系列的方法來(lái)完成相應的請求處理和轉向操作.其中有一個(gè)方法引起了我的關(guān)注,就是processLocale()方法.
Struts框架:RequestProcess類(lèi)中的processLocale()方法原型如下:
程序代碼:
protected void processLocale(HttpServletRequest request,
HttpServletResponse response) {
// Are we configured to select the Locale automatically?
if (!moduleConfig.getControllerConfig().getLocale()) {
return;
}
// Has a Locale already been selected?
HttpSession session = request.getSession();
if (session.getAttribute(Globals.LOCALE_KEY) != null) {
return;
}
// Use the Locale returned by the servlet container (if any)
Locale locale = request.getLocale();
if (locale != null) {
if (log.isDebugEnabled()) {
log.debug(" Setting user locale '" + locale + "'");
}
session.setAttribute(Globals.LOCALE_KEY, locale);
}
}
此類(lèi)在struts-config.xml配置文件中有對應的配置項: < controller locale="true">< /controller> 其缺省狀態(tài)locale屬性的值為true,也就會(huì )調用processLocale方法,并在第一次請求時(shí)創(chuàng )建session對象和生成 sessionId.但改為false后,在第一次請求到達ActionServlet后不會(huì )調用processLocale方法,也就不會(huì )生成 session對象了。
結果也就出來(lái)了,在struts應用中,*.do到達server端后經(jīng)過(guò)ActionServlet后轉想我們自己寫(xiě)的IndexAction之前, < controller locale="true">< /controller>(缺省狀態(tài)) 時(shí),就已經(jīng)產(chǎn)生了session對象和sessionId,這是struts框架類(lèi)中生成的,即使我們在IndexAction中寫(xiě)上 HttpSession session = request.getSession();其也是RequestProcess類(lèi)中的processLocale()方法生成的,此時(shí)其session 的isNew也還是true,因為還沒(méi)有返回客戶(hù)端,其是新創(chuàng )建的,那么按照上面的流程,當在login.jsp上通過(guò)login.do進(jìn)入 LoginAction后,其request.getCookies()固然也就有值了!并且其值是RequestProcess類(lèi)中的 processLocale()方法產(chǎn)生session對象時(shí)生成的.
如果我們在struts-config.xml中加上< controller locale="false">< /controller> 時(shí),此時(shí)如果再根據上面的流程來(lái)跟蹤程序,并在LoginAction用request.getCookies()測試時(shí),其值是為null的,當然在 IndexAction寫(xiě)上HttpSession session = request.getSession();時(shí)其是進(jìn)入IndexAction時(shí)新創(chuàng )建的,isNew也是true。
察看了JBOSS的源代碼,命名服務(wù)器拋出的這個(gè)異常分析如下
java.net.SocketException: Software caused connection abort: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1639)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeNonProxyDesc(ObjectOutputStream.java:1146)
at java.io.ObjectOutputStream.writeClassDesc(ObjectOutputStream.java:1100)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1241)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1052)
at java.io.ObjectOutputStream.writeFatalException(ObjectOutputStream.java:1355)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:281)
上述異常是由于這樣的原因造成的:
1、 客戶(hù)端進(jìn)行查找是NamingContext會(huì )建立到命名服務(wù)器的Socket連接。(此連接是帶讀取超時(shí)的?。?div style="height:15px;">
2、 服務(wù)器接收了客戶(hù)端的連接,使客戶(hù)端可以繼續向下運行。于是客戶(hù)端運行到ObjectInputStream的readObject處,并等待。此時(shí),客戶(hù)端是想要得到NamingServer的stub。
3、 服務(wù)端由于線(xiàn)程繁忙,遲遲不能將客戶(hù)端需要的stub寫(xiě)入ObjectOutputStream。于是客戶(hù)端等待超時(shí),然后客戶(hù)端拋出異常。如果此查找操作是在登錄操作,客戶(hù)在登錄失敗后選擇推出程序。則Socket被關(guān)閉。
4、 服務(wù)端閑下來(lái)后調用ObjectOutputStream的writeObject方法,此時(shí)由于客戶(hù)端Socket關(guān)閉,最終拋出上述異常。
這是我們公司的一個(gè)牛人分析的,后來(lái)察看了JMS也存在類(lèi)似問(wèn)題。另:JBOSS的源代碼質(zhì)量不高,JNDI中存在socket未關(guān)閉的情況,JMS代碼中socket用法也很不規范。大家小心了