Gay Bird

          登高者必自卑,行遠(yuǎn)者必自邇,在這個(gè)世界上,重要的不是你正站在那里,而是你正朝什么方向移動(dòng)......

          編程經(jīng)驗(yàn)系列-Java學(xué)習(xí)雜談(九)--Struts

           J2ee的開(kāi)源框架很多,筆者只能介紹自己熟悉的幾個(gè),其他的目前在中國(guó)IT行業(yè)應(yīng)用得不是很多。希望大家對(duì)新出的框架不要盲目的推崇,首先一定要熟悉它比舊的到底好在哪里,新的理念和特性是什么?然后再?zèng)Q定是否要使用它。

              這期的主題是Struts,直譯過(guò)來(lái)是支架。Struts的第一個(gè)版本是在2001年5月發(fā)布的,它提供了一個(gè)Web應(yīng)用的解決方案,如何讓Jsp和 servlet共存去提供清晰的分離視圖和業(yè)務(wù)應(yīng)用邏輯的架構(gòu)。在Struts之前,通常的做法是在Jsp中加入業(yè)務(wù)邏輯,或者在Servlet中生成視圖轉(zhuǎn)發(fā)到前臺(tái)去。Struts帶著MVC的新理念當(dāng)時(shí)退出幾乎成為業(yè)界公認(rèn)的Web應(yīng)用標(biāo)準(zhǔn),于是當(dāng)代IT市場(chǎng)上也出現(xiàn)了眾多熟悉Struts的程序員。即使有新的框架再出來(lái)不用,而繼續(xù)用Struts的理由也加上了一條低風(fēng)險(xiǎn),因?yàn)橹型救绻_(kāi)發(fā)人員變動(dòng),很容易的招進(jìn)新的會(huì)Struts的IT民工啊, ^_^!

              筆者之前說(shuō)的都是Struts-1,因?yàn)樾鲁隽薙truts-2,使得每次談到Struts都必須注明它是Struts-1還是2.筆者先談比較熟悉的 Struts-1,下次再介紹一下與Struts-2的區(qū)別:

              1.   Struts框架整體結(jié)構(gòu)

              Struts-1的核心功能是前端控制器,程序員需要關(guān)注的是后端控制器。前端控制器是是一個(gè)Servlet,在Web.xml中間配置所有 Request都必須經(jīng)過(guò)前端控制器,它的名字是ActionServlet,由框架來(lái)實(shí)現(xiàn)和管理。所有的視圖和業(yè)務(wù)邏輯隔離都是應(yīng)為這個(gè) ActionServlet,       它就像一個(gè)交通警察,所有過(guò)往的車(chē)輛必須經(jīng)過(guò)它的法眼,然后被送往特定的通道。所有,對(duì)它的理解就是分發(fā)器,我們也可以叫做Dispatcher,其實(shí)了解Servlet編程的人自己也可以寫(xiě)一個(gè)分發(fā)器,加上攔截request的Filter,其實(shí)自己實(shí)現(xiàn)一個(gè)struts框架并不是很困難。主要目的就是讓編寫(xiě)視圖的和后臺(tái)邏輯的可以脫離緊耦合,各自同步的完成自己的工作。

              那么有了ActionServlet在中間負(fù)責(zé)轉(zhuǎn)發(fā),前端的視圖比如說(shuō)是Jsp,只需要把所有的數(shù)據(jù)Submit,這些數(shù)據(jù)就會(huì)到達(dá)適合處理它的后端控制器Action,然后在里面進(jìn)行處理,處理完畢之后轉(zhuǎn)發(fā)到前臺(tái)的同一個(gè)或者不同的視圖Jsp中間,返回前臺(tái)利用的也是Servlet里面的forward 和redirect兩種方式。所以到目前為止,一切都只是借用了Servlet的API搭建起了一個(gè)方便的框架而已。這也是Struts最顯著的特性?? 控制器。

              那么另外一個(gè)特性,可以說(shuō)也是Struts-1帶來(lái)的一個(gè)比較成功的理念,就是以xml配置代替硬編碼配置信息。以往決定Jsp往哪個(gè)servlet提交,是要寫(xiě)進(jìn)Jsp代碼中的,也就是說(shuō)一旦這個(gè)提交路徑要改,我們必須改寫(xiě)代碼再重新編譯。而Struts提出來(lái)的思路是,編碼的只是一個(gè)邏輯名字,它對(duì)應(yīng)哪個(gè)class文件寫(xiě)進(jìn)了xml配置文件中,這個(gè)配置文件記錄著所有的映射關(guān)系,一旦需要改變路徑,改變xml文件比改變代碼要容易得多。這個(gè)理念可以說(shuō)相當(dāng)成功,以致于后來(lái)的框架都延續(xù)著這個(gè)思路,xml所起的作用也越來(lái)越大。

              大致上來(lái)說(shuō)Struts當(dāng)初給我們帶來(lái)的新鮮感就這么多了,其他的所有特性都是基于方便的控制轉(zhuǎn)發(fā)和可擴(kuò)展的xml配置的基礎(chǔ)之上來(lái)完成它們的功能的。

              下面將分別介紹Action和FormBean,       這兩個(gè)是Struts中最核心的兩個(gè)組件。
          2.   后端控制器Action

              Action就是我們說(shuō)的后端控制器,它必須繼承自一個(gè)Action父類(lèi),Struts設(shè)計(jì)了很多種Action,例如DispatchAction、 DynaValidationAction.它們都有一個(gè)處理業(yè)務(wù)邏輯的方法execute(),傳入的request,       response,       formBean和actionMapping四個(gè)對(duì)象,返回actionForward對(duì)象。到達(dá)Action之前先會(huì)經(jīng)過(guò)一個(gè) RequestProcessor來(lái)初始化配置文件的映射關(guān)系,這里需要大家注意幾點(diǎn):

              1)   為了確保線程安全,在一個(gè)應(yīng)用的生命周期中,Struts框架只會(huì)為每個(gè)Action類(lèi)創(chuàng)建一個(gè)Action實(shí)例,所有的客戶(hù)請(qǐng)求共享同一個(gè)Action 實(shí)例,并且所有線程可以同時(shí)執(zhí)行它的execute()方法。所以當(dāng)你繼承父類(lèi)Action,并添加了private成員變量的時(shí)候,請(qǐng)記住這個(gè)變量可以被多個(gè)線程訪問(wèn),它的同步必須由程序員負(fù)責(zé)。(所有我們不推薦這樣做)。在使用Action的時(shí)候,保證線程安全的重要原則是在Action類(lèi)中僅僅使用局部變量,謹(jǐn)慎的使用實(shí)例變量。局部變量是對(duì)每個(gè)線程來(lái)說(shuō)私有的,execute方法結(jié)束就被銷(xiāo)毀,而實(shí)例變量相當(dāng)于被所有線程共享。

              2)   當(dāng)ActionServlet實(shí)例接收到Http請(qǐng)求后,在doGet()或者doPost()方法中都會(huì)調(diào)用process()方法來(lái)處理請(qǐng)求。 RequestProcessor類(lèi)包含一個(gè)HashMap,作為存放所有Action實(shí)例的緩存,每個(gè)Action實(shí)例在緩存中存放的屬性key為 Action類(lèi)名。在RequestProcessor類(lèi)的processActionCreate()方法中,首先檢查在HashMap中是否存在 Action實(shí)例。創(chuàng)建Action實(shí)例的代碼位于同步代碼塊中,以保證只有一個(gè)線程創(chuàng)建Action實(shí)例。一旦線程創(chuàng)建了Action實(shí)例并把它存放到 HashMap中,以后所有的線程會(huì)直接使用這個(gè)緩存中的實(shí)例。

              3)   <action>       元素的   <roles>   屬性指定訪問(wèn)這個(gè)Action用戶(hù)必須具備的安全角色,多個(gè)角色之間逗號(hào)隔開(kāi)。RequestProcessor類(lèi)在預(yù)處理請(qǐng)求時(shí)會(huì)調(diào)用自身的 processRoles()方法,檢查配置文件中是否為Action配置了安全角色,如果有,就調(diào)用HttpServletRequest的 isUserInRole()方法來(lái)判斷用戶(hù)是否具備了必要的安全性角色,如果不具備,就直接向客戶(hù)端返回錯(cuò)誤。(返回的視圖通過(guò)   <input>   屬性來(lái)指定)

              3.   數(shù)據(jù)傳輸對(duì)象FormBean

              Struts并沒(méi)有把模型層的業(yè)務(wù)對(duì)象直接傳遞到視圖層,而是采用DTO(Data       Transfer       Object)來(lái)傳輸數(shù)據(jù),這樣可以減少傳輸數(shù)據(jù)的冗余,提高傳輸效率;還有助于實(shí)現(xiàn)各層之間的獨(dú)立,使每個(gè)層分工明確。Struts的DTO就是 ActionForm,即formBean.由于模型層應(yīng)該和Web應(yīng)用層保持獨(dú)立。由于ActionForm類(lèi)中使用了Servlet       API,       因此不提倡把ActionForm傳遞給模型層,       而應(yīng)該在控制層把ActionForm       Bean的數(shù)據(jù)重新組裝到自定義的DTO中,       再把它傳遞給模型層。它只有兩個(gè)scope,分別是session和request.(默認(rèn)是session)一個(gè)ActionForm標(biāo)準(zhǔn)的生命周期是:1)   控制器收到請(qǐng)求       -> 2)   從request或session中取出ActionForm實(shí)例,如不存在就創(chuàng)建一個(gè)       -> 3)   調(diào)用ActionForm的reset()方法       -> 4)   把實(shí)例放入session或者request中       -> 5)   將用戶(hù)輸入表達(dá)數(shù)據(jù)組裝到ActionForm中       -> 6)   如眼張方法配置了就調(diào)用validate()方法           -> 7)   如驗(yàn)證錯(cuò)誤就轉(zhuǎn)發(fā)給   <input>   屬性指定的地方,否則調(diào)用execute()方法

              validate()方法調(diào)用必須滿足兩個(gè)條件:1)   ActionForm       配置了Action映射而且name屬性匹配2)   <aciton>       元素的validate屬性為true

              如果ActionForm在request范圍內(nèi),那么對(duì)于每個(gè)新的請(qǐng)求都會(huì)創(chuàng)建新的ActionForm實(shí)例,屬性被初始化為默認(rèn)值,那么reset ()方法就顯得沒(méi)有必要;但如果ActionForm在session范圍內(nèi),同一個(gè)ActionForm實(shí)例會(huì)被多個(gè)請(qǐng)求共享,reset()方法在這種情況下極為有用。

          4.   驗(yàn)證框架和國(guó)際化

              Struts有許多自己的特性,但是基本上大家還是不太常用,說(shuō)白了它們也是基于JDK中間的很多Java基礎(chǔ)包來(lái)完成工作。例如國(guó)際化、驗(yàn)證框架、插件自擴(kuò)展功能、與其他框架的集成、因?yàn)楦鞔罂蚣芑径加刑峁┻@樣的特性,Struts也并不是做得最好的一個(gè),這里也不想多說(shuō)。Struts的驗(yàn)證框架,是通過(guò)一個(gè)validator.xml的配置文件讀入驗(yàn)證規(guī)則,然后在validation-rules.xml里面找到驗(yàn)證實(shí)現(xiàn)通過(guò)自動(dòng)為Jsp插入 Javascript來(lái)實(shí)現(xiàn),可以說(shuō)做得相當(dāng)簡(jiǎn)陋。彈出來(lái)的JavaScript框不但難看還很多冗余信息,筆者寧愿用formBean驗(yàn)證或者 Action的saveErrors(),驗(yàn)證邏輯雖然要自己寫(xiě),但頁(yè)面隱藏/浮現(xiàn)的警告提示更加人性化和美觀一些。

              至于Struts的國(guó)際化,其實(shí)無(wú)論哪個(gè)框架的國(guó)際化,java.util.Locale類(lèi)是最重要的Java       I18N類(lèi)。在Java語(yǔ)言中,幾乎所有的對(duì)國(guó)際化和本地化的支持都依賴(lài)于這個(gè)類(lèi)。如果Java類(lèi)庫(kù)中的某個(gè)類(lèi)在運(yùn)行的時(shí)候需要根據(jù)Locale對(duì)象來(lái)調(diào)整其功能,那么就稱(chēng)這個(gè)類(lèi)是本地敏感的(Locale-Sensitive),       例如java.text.DateFormat類(lèi)就是,依賴(lài)于特定Locale.

              創(chuàng)建Locale對(duì)象的時(shí)候,需要明確的指定其語(yǔ)言和國(guó)家的代碼,語(yǔ)言代碼遵從的是ISO-639規(guī)范,國(guó)家代碼遵從ISO-3166規(guī)范,可以從http://www.unicode.org/unicode/onlinedat/languages.html http://www.unicode.org/unicode/onlinedat/countries.htm

              Struts的國(guó)際化是基于properties的message/key對(duì)應(yīng)來(lái)實(shí)現(xiàn)的,筆者曾寫(xiě)過(guò)一個(gè)程序,所有Jsp頁(yè)面上沒(méi)有任何Text文本串,全部都用的是   <bean:message>   去Properties文件里面讀,這個(gè)時(shí)候其實(shí)只要指定不同的語(yǔ)言區(qū)域讀不同的Properties文件就實(shí)現(xiàn)了國(guó)際化。需要注意的是不同語(yǔ)言的字符寫(xiě)進(jìn)Properties文件的時(shí)候需要轉(zhuǎn)化成Unicode碼,JDK已經(jīng)帶有轉(zhuǎn)換的功能。JDK的bin目錄中有native2ascii這個(gè)命令,可以完成對(duì)*.txt和*.properties的Unicode碼轉(zhuǎn)換。

          posted on 2008-09-12 17:26 Sky Yi 閱讀(180) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): 編程經(jīng)驗(yàn)系列-Java學(xué)習(xí)雜談(轉(zhuǎn))

          主站蜘蛛池模板: 皮山县| 万盛区| 惠安县| 民和| 霸州市| 抚远县| 昌平区| 邹城市| 义乌市| 晋州市| 凉城县| 安徽省| 咸丰县| 饶平县| 新绛县| 满城县| 浑源县| 交城县| 隆化县| 格尔木市| 泽州县| 元江| 紫金县| 南漳县| 鄂尔多斯市| 耒阳市| 彭泽县| 霍州市| 镇原县| 聊城市| 东城区| 虞城县| 五原县| 遂溪县| 锡林浩特市| 兴海县| 平泉县| 丹阳市| 华容县| 遂宁市| 织金县|