Struts 2為大家提供了不少常用的很酷的表單標(biāo)志,簡(jiǎn)化了我們程序員的工作。不過(guò),由于這些都是新標(biāo)志,大家可能在使用上還存在不少疑問(wèn)。本文將就朋友們的回復(fù)、留言或Email上的問(wèn)題,分別對(duì)這些酷標(biāo)志進(jìn)行講述。
Struts 2的表單標(biāo)志在輸出(render)HTML時(shí),使用了模板的概念,增加了復(fù)雜性(因?yàn)樗幌馭truts 1.x的表單標(biāo)志,它通常都是一個(gè)標(biāo)志對(duì)應(yīng)HTML的一個(gè)元素),因此大家在使用時(shí),需要一些技巧:
下面我將分別對(duì)這些標(biāo)志進(jìn)行講述:
大家對(duì)<s:checkboxlist />的最大的疑問(wèn)可能是:“如何在默認(rèn)情況下,選中某些checkbox?”
答案其實(shí)很簡(jiǎn)單,只需要將其“value”屬性設(shè)為你的要選中的值,如以代碼所示:
分布運(yùn)行應(yīng)用程序,在瀏覽器中鍵入:http://localhost:8080/Struts2_CoolTags/checkboxlist.jsp,出現(xiàn)如下圖所示頁(yè)面:
清單2 checkboxlist.jsp頁(yè)面
大家看Struts 2的showcase的例子,<s:doubleselect />的用法如下所示:
很多朋友問(wèn):“上面的‘list’屬性只有兩個(gè)值,如果我有三個(gè)或更多的值,‘doublelist’屬性應(yīng)該如何設(shè)定呢?”
我建議的做法是先定義一個(gè)Map類(lèi)型的對(duì)象,鍵為“list”的集合,值則為“doubleList”的集合,然后“doubleList”的OGNL寫(xiě)成“#myMap[top]”,如以下代碼所示:
分布運(yùn)行應(yīng)用程序,在瀏覽器中鍵入:http://localhost:8080/Struts2_CoolTags/doubleselect.action,出現(xiàn)如下圖所示頁(yè)面:
清單5 doubleselect.jsp頁(yè)面
這個(gè)標(biāo)志可能大家不常用,不過(guò)本人認(rèn)為它還是挺有用的。在使用Struts 1.x時(shí),因?yàn)樘D(zhuǎn)通常是用Forward(而不是Redirect)實(shí)現(xiàn)的,所以當(dāng)用戶(hù)完成請(qǐng)求后,按“F5”刷新頁(yè)面時(shí),就會(huì)重新提交上次的請(qǐng)求,這樣經(jīng)常會(huì)出錯(cuò)。要解決這個(gè)問(wèn)題,<s:token />可以幫你忙。
在頁(yè)面加載時(shí),<s: token />產(chǎn)生一個(gè)GUID(Globally Unique Identifier,全局唯一標(biāo)識(shí)符)值的隱藏輸入框如:
同時(shí),將GUID放到會(huì)話(huà)(session)中;在執(zhí)行action之前,“token”攔截器將會(huì)話(huà)token與請(qǐng)求token比較,如果兩者相同,則將會(huì)話(huà)中的token刪除并往下執(zhí)行,否則向actionErrors加入錯(cuò)誤信息。如此一來(lái),如果用戶(hù)通過(guò)某種手段提交了兩次相同的請(qǐng)求,兩個(gè)token就會(huì)不同。
首先看一下Action的代碼:
以上代碼一目了然,再看看JSP的寫(xiě)法:
JSP也很簡(jiǎn)單,就是加入<s:token />標(biāo)志。接下來(lái)是Actoin配置的XML片段:
以上XML片段值注意的是加入了“token”攔截器和“invalid.token”結(jié)果,因?yàn)?#8220;token”攔截器在會(huì)話(huà)token與請(qǐng)求token不一致時(shí),將會(huì)直接返回“invalid.token”結(jié)果。
發(fā)布運(yùn)行應(yīng)用程序,在瀏覽器中鍵入:http://localhost:8080/Struts2_CoolTags/token.jsp,出現(xiàn)如下圖所示頁(yè)面:
清單10 正常顯示的token.jsp頁(yè)面
隨便填點(diǎn)東西并提交頁(yè)面,一切正常返回以上頁(yè)面,然后按“F5”刷新頁(yè)面,在彈出的對(duì)話(huà)框中點(diǎn)擊“Retry”,出現(xiàn)如下圖所示頁(yè)面:
清單11 重復(fù)提交出錯(cuò)顯示
這幾個(gè)標(biāo)志的使用相對(duì)簡(jiǎn)單,所以我想小舉一例即可,以下是JSP的代碼:
發(fā)布運(yùn)行應(yīng)用程序,在瀏覽器中鍵入:http://localhost:8080/Struts2_CoolTags/others.jsp,出現(xiàn)如下圖所示頁(yè)面:
Struts 2在標(biāo)志上的確比Struts 1.x豐富了許多,同時(shí)模板機(jī)制也給程序員帶來(lái)不少方便(如果你不太喜歡個(gè)性化的風(fēng)格)。另外,Struts 2還有一些AJAX(如<s: autocompleter />等)的標(biāo)志和非表單的UI標(biāo)志(如<s: tree />等),我會(huì)在以后的文章中講述其使用。
IoC(Inversion of Control,以下譯為控制反轉(zhuǎn))隨著Java社區(qū)中輕量級(jí)容器(Lightweight Contianer)的推廣而越來(lái)越為大家耳熟能詳。在此,我不想再多費(fèi)唇舌來(lái)解釋“什么是控制反轉(zhuǎn)”和“為什么需要控制反轉(zhuǎn)”。因?yàn)榛ヂ?lián)網(wǎng)上已經(jīng)有非常多的文章對(duì)諸如此類(lèi)的問(wèn)題作了精彩而準(zhǔn)確的回答。大家可以去讀一下Rod Johnson和Juergen Hoeller合著的《Expert one-on-one J2EE Development without EJB》或Martin Fowler所寫(xiě)的《Inversion of Control Containers and the Dependency Injection pattern》。
言歸正傳,本文的目的主要是介紹在Struts 2中實(shí)現(xiàn)控制反轉(zhuǎn)。
眾所周知,Struts 2是以Webwork 2作為基礎(chǔ)發(fā)展出來(lái)。而在Webwork 2.2之前的Webwork版本,其自身有一套控制反轉(zhuǎn)的實(shí)現(xiàn),Webwork 2.2在Spring 框架的如火如荼發(fā)展的背景下,決定放棄控制反轉(zhuǎn)功能的開(kāi)發(fā),轉(zhuǎn)由Spring實(shí)現(xiàn)。值得一提的是,Spring確實(shí)是一個(gè)值得學(xué)習(xí)的框架,因?yàn)橛性絹?lái)越多的開(kāi)源組件(如iBATIS等)都放棄與Spring重疊的功能的開(kāi)發(fā)。因此,Struts 2推薦大家通過(guò)Spring實(shí)現(xiàn)控制反轉(zhuǎn)。
首先,在開(kāi)發(fā)環(huán)境中配置好Struts 2的工程。對(duì)這部分仍然有問(wèn)題的朋友,請(qǐng)參考我的早前的文章。
然后,將所需的Spring的jar包加入到工程的構(gòu)建環(huán)境(Build Path)中,如下圖1所示:
圖1 所依賴(lài)的Spring的jar包
本文使用的是Spring 2.0,Spring強(qiáng)烈建議大家在使用其jar包時(shí),只引用需要的包,原因是Spring是一個(gè)功能非常強(qiáng)大的框架,其中有些功能是您不需要的;而且Spring提倡的是“按需所取”,而不是EJB的“愛(ài)我就要愛(ài)我的一切”。當(dāng)然,如果你怕麻煩或者是不清楚每個(gè)包的作用,引用一個(gè)Spring的總包也未嘗不可。
接下來(lái),就要修改WEB-INF\web.xml文件了,內(nèi)容為:
大家一看便知道,主要是加入Spring的ContextLoaderListener監(jiān)聽(tīng)器,方便Spring與Web容器交互。
緊接著,修改Struts.properties文件,告知Struts 2運(yùn)行時(shí)使用Spring來(lái)創(chuàng)建對(duì)象(如Action等),內(nèi)容如下:
再下來(lái),遵循Spring的原則——面向接口編程,創(chuàng)建接口ChatService,代碼如下:
然后,再創(chuàng)建一個(gè)默認(rèn)實(shí)現(xiàn)ChatServiceImpl,代碼如下:
接下來(lái),就該新建Action了。tutorial.ChatAction.java的代碼如下:
ChatAction類(lèi)使用屬性(Getter/Setter)注入法取得ChatService對(duì)象。
然后,配置Spring的applicationContext.xml(位于WEB-INF下)文件,內(nèi)容如下:
上述代碼有二點(diǎn)值得大家注意的:
接下來(lái),在classes/struts.xml中配置Action,內(nèi)容如下:
這里的Action和平常不同的就是class屬性,它對(duì)應(yīng)于Spring所定義的bean的id,而不是它的類(lèi)全名。
最后,讓我們看看/UserList.jsp,內(nèi)容如下:
大功告成,分布運(yùn)行應(yīng)用程序,在瀏覽器中鍵入http://localhost:8080/Struts2_IoC/Chat.action,出現(xiàn)如圖2所示頁(yè)面:
圖2 /ListUser.jsp
通過(guò)Spring在Struts 2上實(shí)現(xiàn)控制反轉(zhuǎn)是強(qiáng)烈推薦的做法,當(dāng)然您也可以組合其它的實(shí)現(xiàn)(如Pico等)。
國(guó)際化是商業(yè)系統(tǒng)中不可或缺的一部分,所以無(wú)論您學(xué)習(xí)的是什么Web框架,它都是必須掌握的技能。
其實(shí),Struts 1.x在此部分已經(jīng)做得相當(dāng)不錯(cuò)了。它極大地簡(jiǎn)化了我們程序員在做國(guó)際化時(shí)所需的工作,例如,如果您要輸出一條國(guó)際化的信息,只需在代碼包中加入FILE-NAME_xx_XX.properties(其中FILE-NAME為默認(rèn)資源文件的文件名),然后在struts-config.xml中指明其路徑,再在頁(yè)面用<bean:message>標(biāo)志輸出即可。
不過(guò),所謂“沒(méi)有最好,只有更好”。Struts 2.0并沒(méi)有在這部分止步,而是在原有的簡(jiǎn)單易用的基礎(chǔ)上,將其做得更靈活、更強(qiáng)大。
下面讓我們看一個(gè)例子——HelloWorld。這個(gè)例子演示如何根據(jù)用戶(hù)瀏覽器的設(shè)置輸出相應(yīng)的HelloWorld。
![]() |
Struts 2.0有兩個(gè)配置文件,struts.xml和struts.properties都是放在WEB-INF/classes/下。
|
![]() |
在此想和大家分享一個(gè)不錯(cuò)的編寫(xiě)properties文件的Eclipse插件(plugin),有了它我們?cè)诰庉嬕恍┖?jiǎn)體中文、繁體中文等Unicode文本時(shí),就不必再使用native2ascii編碼了。您可以通過(guò)Eclipse中的軟件升級(jí)(Software Update)安裝此插件,步驟如下: 1、展開(kāi)Eclipse的Help菜單,將鼠標(biāo)移到Software Update子項(xiàng),在出現(xiàn)的子菜單中點(diǎn)擊Find and Install;
2、在Install/Update對(duì)話(huà)框中選擇Search for new features to install,點(diǎn)擊Next; 3、在Install對(duì)話(huà)框中點(diǎn)擊New Remote Site; 4、在New Update Site對(duì)話(huà)框的Name填入“PropEdit”或其它任意非空字符串,在URL中填入http://propedit.sourceforge.jp/eclipse/updates/; 5、在Site to include to search列表中,除上一步加入的site外的其它選項(xiàng)去掉,點(diǎn)擊Finsih; 6、在彈出的Updates對(duì)話(huà)框中的Select the features to install列表中將所有結(jié)尾為“3.1.x”的選項(xiàng)去掉(適用于Eclipse 3.2版本的朋友); 7、點(diǎn)擊Finish關(guān)閉對(duì)話(huà)框; 8、在下載后,同意安裝,再按提示重啟Eclipse,在工具條看到形似vi的按鈕表示安裝成功,插件可用。此時(shí),Eclpise中所有properties文件的文件名前有綠色的P的圖標(biāo)作為標(biāo)識(shí)。 |
之所以說(shuō)Struts 2.0的國(guó)際化更靈活是因?yàn)樗梢阅芨鶕?jù)不同需要配置和獲取資源(properties)文件。在Struts 2.0中有下面幾種方法:
上面我列舉了四種配置和訪(fǎng)問(wèn)資源的方法,它們的范圍分別是從大到小,而Struts 2.0在查找國(guó)際化字符串所遵循的是特定的順序,如圖3所示:
假設(shè)我們?cè)谀硞€(gè)ChildAction中調(diào)用了getText("user.title"),Struts 2.0的將會(huì)執(zhí)行以下的操作:
許多情況下,我們都需要在動(dòng)行時(shí)(runtime)為國(guó)際化字符插入一些參數(shù),例如在輸入驗(yàn)證提示信息的時(shí)候。在Struts 2.0中,我們通過(guò)以下兩種方法做到這點(diǎn):
開(kāi)發(fā)國(guó)際化的應(yīng)用程序時(shí),有一個(gè)功能是必不可少的——讓用戶(hù)快捷地選擇或切換語(yǔ)言。在Struts 2.0中,通過(guò)ActionContext.getContext().setLocale(Locale arg)可以設(shè)置用戶(hù)的默認(rèn)語(yǔ)言。不過(guò),由于這是一個(gè)比較普遍的應(yīng)用場(chǎng)景(Scenario),所以Struts 2.0為您提供了一個(gè)名i18n的攔截器(Interceptor),并在默認(rèn)情況下將其注冊(cè)到攔截器鏈(Interceptor chain)中。它的原理為在執(zhí)行Action方法前,i18n攔截器查找請(qǐng)求中的一個(gè)名為"request_locale"的參數(shù)。如果其存在,攔截器就將其作為參數(shù)實(shí)例化Locale對(duì)象,并將其設(shè)為用戶(hù)默認(rèn)的區(qū)域(Locale),最后,將此Locale對(duì)象保存在session的名為“WW_TRANS_I18N_LOCALE”的屬性中。
下面,我將提供一完整示例演示它的使用方法。
上述代碼的原理為,LangSelector.jsp先實(shí)例化一個(gè)Locales對(duì)象,并把對(duì)象的Map類(lèi)型的屬性locales賦予下拉列表(select) 。如此一來(lái),下拉列表就獲得可用語(yǔ)言的列表。大家看到LangSelector有<s:form>標(biāo)志和一段Javascript腳本,它們的作用就是在用戶(hù)在下拉列表中選擇了后,提交包含“reqeust_locale”變量的表單到Action。在打開(kāi)頁(yè)面時(shí),為了下拉列表的選中的當(dāng)前區(qū)域,我們需要到session取得當(dāng)前區(qū)域(鍵為“WW_TRANS_I18N_LOCALE”的屬性),而該屬性在沒(méi)有設(shè)置語(yǔ)言前是為空的,所以通過(guò)值棧中l(wèi)ocale屬性來(lái)取得當(dāng)前區(qū)域(用戶(hù)瀏覽器所設(shè)置的語(yǔ)言)。
你可以把LangSelector.jsp作為一個(gè)控件使用,方法是在JSP頁(yè)面中把它包含進(jìn)來(lái),代碼如下所示:![]() |
可能大家會(huì)問(wèn)為什么一定要通過(guò)Action來(lái)訪(fǎng)問(wèn)頁(yè)面呢? 你可以試一下不用Action而直接用JSP的地址來(lái)訪(fǎng)問(wèn)頁(yè)面,結(jié)果會(huì)是無(wú)論你在下拉列表中選擇什么,語(yǔ)言都不會(huì)改變。這表示不能正常運(yùn)行的。其原因?yàn)槿绻苯邮褂肑SP訪(fǎng)問(wèn)頁(yè)面,Struts 2.0在web.xml的配置的過(guò)濾器(Filter)就不會(huì)工作,所以攔截器鏈也不會(huì)工作。 |