1. 程式人生 > >java知識體系構建

java知識體系構建

Java雜談(九)--Struts
 
 
J2ee的開源框架很多,筆者只能介紹自己熟悉的幾個,其他的目前在中國IT行業應用得不是很多。希望大家對新出的框架不要盲目的推崇,首先一定要熟悉它比舊的到底好在哪裡,新的理念和特性是什麼?然後再決定是否要使用它。 
 
 
這期的主題是Struts,直譯過來是支架。Struts的第一個版本是在2001年5月釋出的,它提供了一個Web應用的解決方案,如何讓Jsp和
servlet共存去提供清晰的分離檢視和業務應用邏輯的架構。在Struts之前,通常的做法是在Jsp中加入業務邏輯,或者在Servlet中生成視
圖轉發到前臺去。Struts帶著MVC的新理念當時退出幾乎成為業界公認的Web應用標準,於是當代IT市場上也出現了眾多熟悉Struts的程式設計師。
即使有新的框架再出來不用,而繼續用Struts的理由也加上了一條低風險,因為中途如果開發人員變動,很容易的招進新的會Struts的IT民工啊,
^_^! 
 
 
筆者之前說的都是Struts-1,因為新出了Struts-2,使得每次談到Struts都必須註明它是Struts-1還是2。筆者先談比較熟悉的 Struts-1,下次再介紹一下與Struts-2的區別: 
 
 
1. Struts框架整體結構 
 
Struts-1的核心功能是前端控制器,程式設計師需要關注的是後端控制器。前端控制器是是一個Servlet,在Web.xml中間配置所有
Request都必須經過前端控制器,它的名字是ActionServlet,由框架來實現和管理。所有的檢視和業務邏輯隔離都是應為這個
ActionServlet,
它就像一個交通警察,所有過往的車輛必須經過它的法眼,然後被送往特定的通道。所有,對它的理解就是分發器,我們也可以叫做Dispatcher,其實了
解Servlet程式設計的人自己也可以寫一個分發器,加上攔截request的Filter,其實自己實現一個struts框架並不是很困難。主要目的就是
讓編寫檢視的和後臺邏輯的可以脫離緊耦合,各自同步的完成自己的工作。 
 
 
那麼有了ActionServlet在中間負責轉發,前端的檢視比如說是Jsp,只需要把所有的資料Submit,這些資料就會到達適合處理它的後端控制
器Action,然後在裡面進行處理,處理完畢之後轉發到前臺的同一個或者不同的檢視Jsp中間,返回前臺利用的也是Servlet裡面的forward
和redirect兩種方式。所以到目前為止,一切都只是借用了Servlet的API搭建起了一個方便的框架而已。這也是Struts最顯著的特性??
控制器。 
 
 
那麼另外一個特性,可以說也是Struts-1帶來的一個比較成功的理念,就是以xml配置代替硬編碼配置資訊。以往決定Jsp往哪個servlet提
交,是要寫進Jsp程式碼中的,也就是說一旦這個提交路徑要改,我們必須改寫程式碼再重新編譯。而Struts提出來的思路是,編碼的只是一個邏輯名字,它對
應哪個class檔案寫進了xml配置檔案中,這個配置檔案記錄著所有的對映關係,一旦需要改變路徑,改變xml檔案比改變程式碼要容易得多。這個理念可以
說相當成功,以致於後來的框架都延續著這個思路,xml所起的作用也越來越大。 
 
 
大致上來說Struts當初給我們帶來的新鮮感就這麼多了,其他的所有特性都是基於方便的控制轉發和可擴充套件的xml配置的基礎之上來完成它們的功能的。 
 
下面將分別介紹Action和FormBean, 這兩個是Struts中最核心的兩個元件。 
 
 
2. 後端控制器Action 
 
Action就是我們說的後端控制器,它必須繼承自一個Action父類,Struts設計了很多種Action,例如DispatchAction、
DynaValidationAction。它們都有一個處理業務邏輯的方法execute(),傳入的request, response,
formBean和actionMapping四個物件,返回actionForward物件。到達Action之前先會經過一個
RequestProcessor來初始化配置檔案的對映關係,這裡需要大家注意幾點: 
 
 
1)
為了確保執行緒安全,在一個應用的生命週期中,Struts框架只會為每個Action類建立一個Action例項,所有的客戶請求共享同一個Action
例項,並且所有執行緒可以同時執行它的execute()方法。所以當你繼承父類Action,並添加了private成員變數的時候,請記住這個變數可以
被多個執行緒訪問,它的同步必須由程式設計師負責。(所有我們不推薦這樣做)。在使用Action的時候,保證執行緒安全的重要原則是在Action類中僅僅使用
區域性變數,謹慎的使用例項變數。區域性變數是對每個執行緒來說私有的,execute方法結束就被銷燬,而例項變數相當於被所有執行緒共享。 
 
 
2)
當ActionServlet例項接收到Http請求後,在doGet()或者doPost()方法中都會呼叫process()方法來處理請求。
RequestProcessor類包含一個HashMap,作為存放所有Action例項的快取,每個Action例項在快取中存放的屬性key為
Action類名。在RequestProcessor類的processActionCreate()方法中,首先檢查在HashMap中是否存在
Action例項。建立Action例項的程式碼位於同步程式碼塊中,以保證只有一個執行緒建立Action例項。一旦執行緒建立了Action例項並把它存放到
HashMap中,以後所有的執行緒會直接使用這個快取中的例項。 
 
 
3) <action> 元素的 <roles>
屬性指定訪問這個Action使用者必須具備的安全形色,多個角色之間逗號隔開。RequestProcessor類在預處理請求時會呼叫自身的
processRoles()方法,檢查配置檔案中是否為Action配置了安全形色,如果有,就呼叫HttpServletRequest的
isUserInRole()方法來判斷使用者是否具備了必要的安全性角色,如果不具備,就直接向客戶端返回錯誤。(返回的檢視通過
<input> 屬性來指定) 
 
 
3. 資料傳輸物件FormBean 
 
Struts並沒有把模型層的業務物件直接傳遞到檢視層,而是採用DTO(Data Transfer
Object)來傳輸資料,這樣可以減少傳輸資料的冗餘,提高傳輸效率;還有助於實現各層之間的獨立,使每個層分工明確。Struts的DTO就是
ActionForm,即formBean。由於模型層應該和Web應用層保持獨立。由於ActionForm類中使用了Servlet API,
因此不提倡把ActionForm傳遞給模型層, 而應該在控制層把ActionForm Bean的資料重新組裝到自定義的DTO中,
再把它傳遞給模型層。它只有兩個scope,分別是session和request。(預設是session)一個ActionForm標準的生命週期
是: 
 
1) 控制器收到請求 -> 
 
2) 從request或session中取出ActionForm例項,如不存在就建立一個 -> 
 
3) 呼叫ActionForm的reset()方法 -> 
 
4) 把例項放入session或者request中 -> 
 
5) 將使用者輸入表達資料組裝到ActionForm中 -> 
 
6) 如眼張方法配置了就呼叫validate()方法 -> 
 
7) 如驗證錯誤就轉發給 <input> 屬性指定的地方,否則呼叫execute()方法 
 
 
validate()方法呼叫必須滿足兩個條件: 
 
1) ActionForm 配置了Action對映而且name屬性匹配 
 
2) <aciton> 元素的validate屬性為true 
 
 
如果ActionForm在request範圍內,那麼對於每個新的請求都會建立新的ActionForm例項,屬性被初始化為預設值,那麼reset
()方法就顯得沒有必要;但如果ActionForm在session範圍內,同一個ActionForm例項會被多個請求共享,reset()方法在這
種情況下極為有用。 
 
 
4. 驗證框架和國際化 
 
Struts有許多自己的特性,但是基本上大家還是不太常用,說白了它們也是基於JDK中間的很多Java基礎包來完成工作。例如國際化、驗證框架、外掛
自擴充套件功能、與其他框架的整合、因為各大框架基本都有提供這樣的特性,Struts也並不是做得最好的一個,這裡也不想多說。Struts的驗證框架,是
通過一個validator.xml的配置檔案讀入驗證規則,然後在validation-rules.xml裡面找到驗證實現通過自動為Jsp插入
Javascript來實現,可以說做得相當簡陋。彈出來的JavaScript框不但難看還很多冗餘資訊,筆者寧願用formBean驗證或者
Action的saveErrors(),驗證邏輯雖然要自己寫,但頁面隱藏/浮現的警告提示更加人性化和美觀一些。 
 
 
至於Struts的國際化,其實無論哪個框架的國際化,java.util.Locale類是最重要的Java
I18N類。在Java語言中,幾乎所有的對國際化和本地化的支援都依賴於這個類。如果Java類庫中的某個類在執行的時候需要根據Locale物件來調
整其功能,那麼就稱這個類是本地敏感的(Locale-Sensitive),
例如java.text.DateFormat類就是,依賴於特定Locale。 
 
 
建立Locale物件的時候,需要明確的指定其語言和國家的程式碼,語言程式碼遵從的是ISO-639規範,國家程式碼遵從ISO-3166規範,可以從 
 
http://www.unicode.org/unicode/onlinedat/languages.html

 
http://www.unicode.org/unicode/onlinedat/countries.htm

 
 
Struts的國際化是基於properties的message/key對應來實現的,筆者曾寫過一個程式,所有Jsp頁面上沒有任何Text文字串,
全部都用的是 <bean:message>
去Properties檔案裡面讀,這個時候其實只要指定不同的語言區域讀不同的Properties檔案就實現了國際化。需要注意的是不同語言的字元寫
進Properties檔案的時候需要轉化成Unicode碼,JDK已經帶有轉換的功能。JDK的bin目錄中有native2ascii這個命令,可
以完成對*.txt和*.properties的Unicode碼轉換。 
 
 
OK,今天就說到這裡,本文中的很多內容也不是筆者的手筆,是筆者一路學習過來自己抄下來的筆記,希望對大家有幫助!Java雜談一路走來,感謝大家持續的關注,大概再有個2到3篇續篇就改完結了!筆者儘快整理完成後續的寫作吧……^_^