Struts的几个精细之处
生活随笔
收集整理的這篇文章主要介紹了
Struts的几个精细之处
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
| J2EE相關(guān): Struts的幾個(gè)精細(xì)之處 發(fā)表于 Friday, April 23 @ 19:32:27 CST by joezxh |
Joe.zhang 投遞 "摘自賽迪網(wǎng) 作者:王和全 最近在網(wǎng)上看到一篇N. Alex Rupp寫(xiě)的“Beyond MVC: A New Look at the Servlet Infrastructure”文章,意思大致是說(shuō)MVC被Struts等框架錯(cuò)誤地應(yīng)用到了Servlet架構(gòu)中。我想只有對(duì)Struts有足夠的了解再加上在MVC方面有足夠深的功力,才敢發(fā)此言論,不是經(jīng)常聽(tīng)人說(shuō):最熟悉自己的人是你的敵人。本人功力尚淺,沒(méi)有引領(lǐng)風(fēng)潮的能力,而且生活還得繼續(xù),只能先來(lái)熟悉熟悉Struts。
申明: 強(qiáng)烈建議在閱讀本文之前先閱讀一下N. Alex Rupp老兄的文章,如果你贊同他的看法,可能你會(huì)覺(jué)得研究Struts就沒(méi)什么意義了。
說(shuō)明:本文所講的Struts知識(shí)基于Struts 1.1版本,除非特別說(shuō)明,本文中的Struts都特指Struts 1.1這個(gè)版本。
目錄:
精細(xì)之處一:“利用Token解決重復(fù)提交”背后的前提
精細(xì)之處二:頁(yè)面流轉(zhuǎn)控制中的職責(zé)分配
精細(xì)之處一:“利用Token解決重復(fù)提交”背后的前提
我們知道,可以利用同步令牌(Token)機(jī)制來(lái)解決Web應(yīng)用中重復(fù)提交的問(wèn)題,Struts也給出了一個(gè)參考實(shí)現(xiàn)。服務(wù)器端在處理到達(dá)的請(qǐng)求之前,會(huì)將請(qǐng)求中包含的令牌值與保存在當(dāng)前用戶(hù)會(huì)話(huà)中的令牌值進(jìn)行比較,看是否匹配。在處理完該請(qǐng)求后,且在答復(fù)發(fā)送給客戶(hù)端之前,將會(huì)產(chǎn)生一個(gè)新的令牌,該令牌除傳給客戶(hù)端以外,也會(huì)將用戶(hù)會(huì)話(huà)中保存的舊的令牌進(jìn)行替換。這樣如果用戶(hù)回退到剛才的提交頁(yè)面并再次提交的話(huà),客戶(hù)端傳過(guò)來(lái)的令牌就和服務(wù)器端的令牌不一致,從而有效地防止了重復(fù)提交的發(fā)生。對(duì)應(yīng)于這段描述,你可能會(huì)在你的Action子類(lèi)中有這么一段代碼:
if (isTokenValid(request, true)) {
// your code here
return mapping.findForward("success");
} else {
saveToken(request);
return mapping.findForward("submitagain");
}
其中isTokenValid()和saveToken()都是org.apache.struts.action.Action類(lèi)中的方法,而具體的Token處理邏輯都在org.apache.struts.util.TokenProcessor類(lèi)中。Struts中是根據(jù)用戶(hù)會(huì)話(huà)ID和當(dāng)前系統(tǒng)時(shí)間來(lái)生成一個(gè)唯一(對(duì)于每個(gè)會(huì)話(huà))令牌的,具體實(shí)現(xiàn)可以參考TokenProcessor類(lèi)中的generateToken()方法。
不知道大家有沒(méi)有注意到這樣一個(gè)問(wèn)題,因?yàn)镾truts是將Token保存在Session的一個(gè)屬性中,也就是說(shuō)對(duì)于每個(gè)會(huì)話(huà)服務(wù)器端只保存而且只能保存一個(gè)最新Token值。對(duì)于這一點(diǎn),我的同事就提出了疑問(wèn):那如果我在同一個(gè)會(huì)話(huà)中打開(kāi)兩個(gè)頁(yè)面,那么后提交的那個(gè)頁(yè)面肯定不能提交成功了。他還給出了一個(gè)實(shí)際的例子:比如現(xiàn)在需要把兩個(gè)客戶(hù)A和B的地址都改為某個(gè)值,那用戶(hù)就可能同時(shí)打開(kāi)兩個(gè)頁(yè)面,修改A,修改B,提交A,提交B,按照Struts中的處理邏輯,B的修改提交就肯定不能成功,但是這個(gè)提交操作對(duì)于用戶(hù)來(lái)說(shuō)并不存在操作不正確的地方。
在這里,可能有人要問(wèn):怎么可能在同一個(gè)會(huì)話(huà)中打開(kāi)兩個(gè)頁(yè)面呢?重新打開(kāi)一個(gè)IE瀏覽器不是重新開(kāi)始了一個(gè)會(huì)話(huà)嗎?不錯(cuò),這種情況下是兩個(gè)會(huì)話(huà),不存在任何問(wèn)題。但是,你還可以通過(guò)菜單“文件”-“新建”-“窗口”(或者快捷鍵Ctrl+N)來(lái)復(fù)制當(dāng)前窗口,這個(gè)時(shí)候你會(huì)發(fā)現(xiàn)該頁(yè)面與原有頁(yè)面同處在一個(gè)會(huì)話(huà)當(dāng)中。其實(shí),能夠發(fā)現(xiàn)這個(gè)問(wèn)題得歸功于我的那位同事對(duì)IE習(xí)慣性的操作方法。
這下我的那位同事不滿(mǎn)意啦,他于是開(kāi)始動(dòng)手修改Struts中的實(shí)現(xiàn)方式,讓每個(gè)頁(yè)面(至少某類(lèi)頁(yè)面)在服務(wù)器端都保存有一個(gè)唯一的Token值。這樣,前面所講的客戶(hù)A,B同時(shí)修改的限制就不存在了。但是不久,我的那位同事就開(kāi)始意識(shí)到他正在走向一條危險(xiǎn)的道路。首先,如果每個(gè)頁(yè)面都在服務(wù)器端保存一個(gè)Token值,則服務(wù)器端保存的數(shù)據(jù)量將越來(lái)越大。而且,如果考慮這種同一個(gè)會(huì)話(huà)中打開(kāi)多個(gè)頁(yè)面的情況的話(huà),就好像打開(kāi)了潘多拉魔盒,將會(huì)給自己帶來(lái)無(wú)窮無(wú)盡的麻煩。比如,首先打開(kāi)頁(yè)面P1,然后利用Ctrl+N得到頁(yè)面P2,P1提交,P2提交,目前為止一切正常。但是如果此時(shí),在P1,P2中點(diǎn)擊“后退”按鈕,然后再提交P1, P2呢,情況會(huì)是怎樣?如果在P2中提交完后執(zhí)行其它操作,而在P1中回退后提交,情況又是怎么樣呢?如果有P1,P2,P3,那情況又是如何呢?太復(fù)雜啦!我想你也會(huì)和我們有同感,你需要考慮許多種可能的組合,而且有的時(shí)候結(jié)果并不是你想象中的那樣簡(jiǎn)單。
此路不通,還得回來(lái)看看Struts。其實(shí)經(jīng)過(guò)以上一番折騰,我們可以發(fā)現(xiàn)在Struts中的Token機(jī)制背后隱藏著這樣一個(gè)前提:不允許你(客戶(hù)端)在同一會(huì)話(huà)中打開(kāi)多個(gè)頁(yè)面。注意是同一會(huì)話(huà),如果打開(kāi)兩個(gè)IE瀏覽器,那已經(jīng)是兩個(gè)會(huì)話(huà)啦,不受該限制。其實(shí),這個(gè)看似不合理的規(guī)定卻自有其道理:一是它極大地簡(jiǎn)化了Token的實(shí)現(xiàn),二個(gè)這種限定也符合大部分人的使用習(xí)慣。
精細(xì)之處二:頁(yè)面流轉(zhuǎn)控制中的職責(zé)分配
我們知道,Struts的執(zhí)行過(guò)程大致如下:首先,控制器接收到客戶(hù)端請(qǐng)求,將這些請(qǐng)求映射至相應(yīng)的Action,并調(diào)用Action的execute方法,這中間可能還涉及到ActionForm的創(chuàng)建和填充。Action的execute方法執(zhí)行完以后,返回一個(gè)ActionForward對(duì)象,控制器根據(jù)該ActionForward對(duì)象將請(qǐng)求轉(zhuǎn)發(fā)至下一個(gè)Action或JSP。最后,產(chǎn)生視圖響應(yīng)客戶(hù)。在大的層面上,Struts是采用了MVC這種架構(gòu),沒(méi)什么特別之處。但從一些小的地方,我們還是可以看出Craig R. McClanahan老兄的一些考慮。我們看到Action與控制器之間傳遞的是ActionForward對(duì)象,由于A(yíng)ction的execute方法要求返回一個(gè)ActionForward對(duì)象,所以你會(huì)經(jīng)常在A(yíng)ction子類(lèi)中看到如下語(yǔ)句:
return (mapping.findForward("success"));
其實(shí)返回的就是一個(gè)ActionForward對(duì)象。在A(yíng)ction中我們根據(jù)程序執(zhí)行的不同情況,決定接下來(lái)的頁(yè)面走向(比如返回到輸入頁(yè)面或者轉(zhuǎn)到下一個(gè)頁(yè)面),并將這些信息保存在A(yíng)ctionForward對(duì)象中。而接下來(lái)控制器就可以直接利用該ActionForward對(duì)象來(lái)進(jìn)行頁(yè)面的流轉(zhuǎn)。下面是org.apache.struts.action.RequestProcessor類(lèi)的processForwardConfig()方法的摘錄,該方法調(diào)用發(fā)生在A(yíng)ction實(shí)例調(diào)用后。
protected void processForwardConfig(HttpServletRequest
request,
HttpServletResponse
response,
ForwardConfig forward)
throws IOException, ServletException {
…
String forwardPath = forward.getPath();
String uri = null;
// paths not starting with /
should be passed through without any processing // (ie. they're absolute)
if (forwardPath.startsWith("/")) {
uri = RequestUtils.forwardURL(request, forward);
// get module relative uri
} else {
uri = forwardPath;
}
if (forward.getRedirect()) {
// only prepend context path for relative uri
if (uri.startsWith("/")) {
uri = request.getContextPath() + uri;
}
response.sendRedirect(response.encodeRedirectURL(uri));
}
else {
doForward(uri, request, response);
}
}
注意: ForwardConfig是ActionForward的父類(lèi)
該方法首先調(diào)用ForwardConfig的getPath()方法獲得下一步流轉(zhuǎn)的路徑,在某些條件下還需要進(jìn)行一些拼裝得到正確的URI,最后根據(jù)該URI進(jìn)行頁(yè)面跳轉(zhuǎn)。可見(jiàn)在processForwardConfig()方法中只是對(duì)ActionForward進(jìn)行了一些“技術(shù)上”的處理,沒(méi)有任何和業(yè)務(wù)相關(guān)的內(nèi)容,這樣就將控制器(ActionServlet)和Action完全分開(kāi)來(lái),兩者互不影響,達(dá)到了功能模塊之間松散耦合的目的。
模塊間(系統(tǒng)間)松散耦合一直是OO設(shè)計(jì)所追求的,但是具體如何去實(shí)現(xiàn)這樣一種松散耦合卻不是那么容易做到的。Struts中的設(shè)計(jì)給了我們一些啟示:模塊間相互關(guān)聯(lián)影響因素的傳遞可以用對(duì)象的形式來(lái)包裝起來(lái)。其實(shí),個(gè)人覺(jué)得Struts中的做法還可以稍微有一點(diǎn)點(diǎn)改進(jìn),就是在A(yíng)ctionForward中提供一個(gè)getURI()方法來(lái)給出最終的URI豈不是更好?
參考:
1、Beyond MVC: A New Look at the Servlet Infrastructure
2、Allen Holub的Build user interfaces for object-oriented systems系列文章,可以從這篇文章中學(xué)到很多面向?qū)ο笤O(shè)計(jì)方面的知識(shí),雖然作者并不認(rèn)為MVC是一種面向?qū)ο蟮姆椒?#xff0c;但是我們這些MVC的實(shí)踐者仍然可以從中學(xué)到面向?qū)ο蟮闹R(shí)。
3、Struts 1.1的介紹性文章:深入Struts 1.1
4、Apache Struts Website
5、關(guān)于重復(fù)提交問(wèn)題的討論及其解決方案,可以參考《Core J2EE Patterns》一書(shū)(中文版《J2EE核心模式》)。
Deepak Alur,John Crupi,Dan Malks: Core J2EE Patterns-Best Practices and Design Strategies
總結(jié)
以上是生活随笔為你收集整理的Struts的几个精细之处的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 语言的进步与代码生成
- 下一篇: 最近工作有点累