- 基于Struts、Hibernate、Spring架構(gòu)的Web應(yīng)用開發(fā)
- 范新燦編著
- 9701字
- 2018-12-30 14:56:29
1.2 Web應(yīng)用的發(fā)展
1.2.1 Web技術(shù)的發(fā)展
隨著Internet技術(shù)的廣泛使用,Web技術(shù)已經(jīng)廣泛應(yīng)用于Internet,但早期的Web應(yīng)用全部是靜態(tài)的HTML頁面,用于將一些文本信息呈現(xiàn)給瀏覽者,但這些信息是固定寫在HTML頁面里的,該頁面不具備與用戶交互的能力,沒有動態(tài)顯示的功能。
于是,人們希望Web應(yīng)用里包含一些能動態(tài)執(zhí)行的頁面,最早的CGI(通用網(wǎng)關(guān)接口)技術(shù)滿足了該要求,CGI技術(shù)使得Web應(yīng)用可以與客戶端瀏覽器交互,不再需要使用靜態(tài)的HTML頁面。CGI技術(shù)可以從數(shù)據(jù)庫中讀取信息,將這些信息呈現(xiàn)給用戶;還可以獲取用戶的請求參數(shù),并將這些參數(shù)保存到數(shù)據(jù)庫里。
CGI技術(shù)開啟了動態(tài)Web應(yīng)用的時代,給予了這種技術(shù)無限的可能性。但CGI技術(shù)存在很多缺點,其中最大的缺點就是開發(fā)動態(tài)Web應(yīng)用難度非常大,而且在性能等各方面也存在限制。到1997年,隨著Java語言的廣泛使用,Servlet技術(shù)迅速成為動態(tài)Web應(yīng)用的主要開發(fā)技術(shù)。與傳統(tǒng)的CGI應(yīng)用相比,Servlet具有大量的優(yōu)勢:
(1)Servlet是基于Java語言創(chuàng)建的,而Java語言則內(nèi)建了多線程支持,這一點大大提高了動態(tài)Web應(yīng)用的性能。
(2)Servlet應(yīng)用可以充分利用Java語言的優(yōu)勢,如JDBC(Java DataBase Connection)等。同時,Java語言提供了豐富的類庫,這些都簡化了Servlet的開發(fā)。
(3)除此之外,Servlet運(yùn)行在Web服務(wù)器中,由Web服務(wù)器去負(fù)責(zé)管理Servlet的實例化,并對客戶端提供多線程、網(wǎng)絡(luò)通信等功能,這都保證Servlet有更好的穩(wěn)定性和性能。
Servlet在Web應(yīng)用中被映射成一個URL(統(tǒng)一資源定位),該URL可以被客戶端瀏覽器請求,當(dāng)用戶向指定URL對應(yīng)的Servlet發(fā)送請求時,該請求被Web服務(wù)器接收到,該Web服務(wù)器負(fù)責(zé)處理多線程、網(wǎng)絡(luò)通信等功能,而Servlet的內(nèi)容則決定了服務(wù)器對客戶端的響應(yīng)內(nèi)容。
圖1-3所示為Servlet的響應(yīng)流程,瀏覽器向Web服務(wù)器內(nèi)指定的Servlet發(fā)送請求,Web服務(wù)器根據(jù)Servlet生成對客戶端的響應(yīng)。

圖1-3 Servlet的響應(yīng)流程
實際上,這是后來所有的動態(tài)Web編程技術(shù)所使用的模型,這種模型都需要一個動態(tài)的程序或一個動態(tài)頁面,當(dāng)客戶端向該動態(tài)程序或動態(tài)頁面發(fā)送請求時,Web服務(wù)器根據(jù)該動態(tài)程序來生成對客戶端的響應(yīng)。
到了1998年,微軟發(fā)布了ASP 2.0,它是Windows NT 4 Option Pack的一部分,作為IIS 4.0的外接式附件。它與ASP 1.0的主要區(qū)別在于它的外部組件是可以初始化的,這樣,在ASP程序內(nèi)部的所有組件都有了獨立的內(nèi)存空間,并可以進(jìn)行事務(wù)處理。這標(biāo)志著ASP技術(shù)開始真正作為動態(tài)Web編程技術(shù)。
當(dāng)ASP技術(shù)在世界上廣泛流行時,人們很快感受到這種簡單技術(shù)的魅力:ASP使用VBScript作為腳本語言,它的語法簡單、開發(fā)效率非常高。而且,世界上已經(jīng)有了非常多的VB程序員,這些VB程序員可以很輕易地過渡成ASP程序員。因此,ASP技術(shù)馬上成為應(yīng)用最廣泛的動態(tài)Web開發(fā)技術(shù)。
隨后,由Sun帶領(lǐng)的Java陣營,立即發(fā)布了JSP標(biāo)準(zhǔn),從某種程度上來看,JSP是Java陣營為了對抗ASP推出的一種動態(tài)Web編程技術(shù)。
ASP和JSP從名稱上如此相似,但它們的運(yùn)行機(jī)制存在一些差別,這主要是因為VBScript是一種腳本語言,無需編譯,而JSP使用Java作為腳本語句,但Java從來就不是解釋型的腳本語言,因此JSP頁面并不能立即執(zhí)行。因此,JSP必須編譯成Servlet,也就是說:JSP的實質(zhì)還是Servlet。不過,書寫JSP比書寫Servlet簡單得多。
JSP的運(yùn)行機(jī)理如圖1-4所示。

圖1-4 JSP的運(yùn)行機(jī)理
對比圖1-3和圖1-4,發(fā)現(xiàn)無論是Servlet動態(tài)Web技術(shù),還是JSP動態(tài)Web技術(shù),它們的實質(zhì)完全一樣。可以這樣理解:JSP是一種更簡單的Servlet技術(shù),這也是JSP技術(shù)出現(xiàn)的意義——作為一個和ASP對抗的技術(shù),簡單就是JSP的最大優(yōu)勢。
隨著實際Web應(yīng)用的使用越來越廣泛,Web應(yīng)用的規(guī)模也越來越大,開發(fā)人員發(fā)現(xiàn)動態(tài)Web應(yīng)用的維護(hù)成本越來越大,即使只需要修改該頁面的一個簡單按鈕文本或一段靜態(tài)的文本內(nèi)容,也不得不打開混雜的動態(tài)腳本的頁面源文件進(jìn)行修改,這是一種很大的風(fēng)險,完全有可能引入新的錯誤。
這個時候,人們意識到,使用單純的ASP或JSP頁面充當(dāng)過多角色是相當(dāng)失敗的選擇,這對于后期的維護(hù)相當(dāng)不利。慢慢地,開發(fā)人員開始在Web開發(fā)中使用MVC模式。
隨后Java陣營發(fā)布了一套完整的企業(yè)開發(fā)規(guī)范:J2EE(現(xiàn)在已經(jīng)更名為Java EE),緊接著,微軟也發(fā)布了ASP.NET技術(shù),它們都采用一種優(yōu)秀的分層思想,力圖解決Web應(yīng)用維護(hù)困難的問題。動態(tài)Web編程技術(shù)的發(fā)展歷史如圖1-5所示。

圖1-5 動態(tài)Web編程技術(shù)的發(fā)展歷史
1.2.2 Model 1和Model 2
Java陣營的動態(tài)Web編程技術(shù)經(jīng)歷了所謂的Model 1和Model 2時代。
Model 1就是JSP大行其道的時代,在Model 1模式下,整個Web應(yīng)用幾乎全部由JSP頁面組成,JSP頁面接收處理客戶端請求,對請求處理后直接作出響應(yīng)。用少量的JavaBean來處理數(shù)據(jù)庫連接、數(shù)據(jù)庫訪問等操作。
圖1-6所示為Model 1的程序流程。

圖1-6 Model 1的程序流程
Model 1模式的實現(xiàn)比較簡單,適用于快速開發(fā)小規(guī)模項目。但從工程化的角度看,它的局限性非常明顯:JSP頁面身兼View和Controller兩種角色,將控制邏輯和表現(xiàn)邏輯混雜在一起,從而導(dǎo)致代碼的重用性非常低,增加了應(yīng)用的擴(kuò)展性和維護(hù)的難度。
早期有大量ASP和JSP技術(shù)開發(fā)出來的Web應(yīng)用,這些Web應(yīng)用都采用了Model 1架構(gòu)。
Model 2已經(jīng)是基于MVC架構(gòu)的設(shè)計模式。在Model 2架構(gòu)中,Servlet作為前端控制器,負(fù)責(zé)接收客戶端發(fā)送的請求,在Servlet中只包含控制邏輯和簡單的前端處理;然后,調(diào)用后端JavaBean來完成實際的邏輯處理;最后,轉(zhuǎn)發(fā)到相應(yīng)的JSP頁面處理顯示邏輯。其具體的實現(xiàn)方式如圖1-7所示。

圖1-7 Model 2的程序流程
從圖1-7中可以看到,Model 2下JSP不再承擔(dān)控制器的責(zé)任,它僅僅是表現(xiàn)層角色,僅僅用于將結(jié)果呈現(xiàn)給用戶,JSP頁面的請求與Servlet(控制器)交互,而Servlet負(fù)責(zé)與后臺的JavaBean通信。在Model 2模式下,模型(Model)由JavaBean充當(dāng),視圖(View)由JSP頁面充當(dāng),而控制器(Controller)則由Servlet充當(dāng)。
由于引入了MVC模式,使Model 2具有組件化的特點,更適用于大規(guī)模應(yīng)用的開發(fā),但也增加了應(yīng)用開發(fā)的復(fù)雜程度。原本需要一個簡單的JSP頁面就能實現(xiàn)的應(yīng)用,在Model 2中被分解成多個協(xié)同工作的部分,需花費更多的時間才能真正掌握其設(shè)計和實現(xiàn)過程。
注意
對于非常小型的Web站點,如果后期的更新、維護(hù)工作不是特別多,可以使用Model 1的模式來開發(fā)應(yīng)用,而不是使用Model 2的模式。雖然Model 2提供了更好的可擴(kuò)展性及可維護(hù)性,但增加了前期開發(fā)成本。從某種程度上講,Model 2降低了系統(tǒng)后期維護(hù)的復(fù)雜度,卻導(dǎo)致了前期開發(fā)的更高復(fù)雜度。
1.2.3 MVC
1.MVC思想
MVC并不是Java語言所特有的設(shè)計思想,也并不是Web應(yīng)用所特有的思想,它是所有面向?qū)ο蟪绦蛟O(shè)計語言都應(yīng)該遵守的規(guī)范。
MVC思想將一個應(yīng)用分成3個基本部分:Model(模型)、View(視圖)和Controller(控制器),這3個部分以最少的耦合協(xié)同工作,從而提高應(yīng)用的可擴(kuò)展性及可維護(hù)性。
最初,MVC模式是針對相同的數(shù)據(jù)需要不同顯示的應(yīng)用而設(shè)計的,其整體效果如圖1-8所示。

圖1-8 MVC結(jié)構(gòu)的整體效果
在MVC模式中,事件由控制器處理,控制器根據(jù)事件的類型改變模型或視圖,反之亦然。具體來說,每個模型對應(yīng)一系列的視圖列表,這種對應(yīng)關(guān)系通常采用注冊來完成,即把多個視圖注冊到同一個模型,當(dāng)模型發(fā)生改變時,模型向所有注冊過的視圖發(fā)送通知,接下來,視圖從對應(yīng)的模型中獲得信息,然后完成視圖顯示的更新。
從設(shè)計模式的角度來看,MVC思想非常類似于一個觀察者模式,但與觀察者模式存在少許差別:在觀察者模式下,觀察者和被觀察者可以是兩個互相對等的對象,但對于MVC思想而言,被觀察者往往只是單純的數(shù)據(jù)體,而觀察者則是單純的視圖頁面。
2.MVC模式的優(yōu)勢
概括起來,MVC有如下優(yōu)勢。
(1)多個視圖可以對應(yīng)一個模型。按MVC設(shè)計模式,一個模型對應(yīng)多個視圖,可以減少代碼的復(fù)制及代碼的維護(hù)量,一旦模型發(fā)生改變,也易于維護(hù)。
(2)模型返回的數(shù)據(jù)與顯示邏輯分離。模型數(shù)據(jù)可以應(yīng)用任何的顯示技術(shù),如使用JSP頁面、Velocity模板或直接產(chǎn)生Excel文檔等。
(3)應(yīng)用被分隔為3層,降低了各層之間的耦合,提高了應(yīng)用的可擴(kuò)展性。
(4)控制層的概念也很有效,由于它把不同的模型和不同的視圖組合在一起,完成不同的請求。因此,控制層可以說是包含了用戶請求權(quán)限的概念。
(5)MVC更符合軟件工程化管理的精神。不同的層各司其職,每一層的組件具有相同的特征,有利于通過工程化和工具化產(chǎn)生管理程序代碼。
3.Web模式下的MVC
相對于早期的MVC思想,經(jīng)典的MVC思想與Web應(yīng)用的MVC思想也存在一定的差別,引起差別的主要原因是Web應(yīng)用是一種請求/響應(yīng)模式下的應(yīng)用,對于請求/響應(yīng)應(yīng)用,如果用戶不對應(yīng)用發(fā)出請求,視圖無法自己主動更新。
對于一個應(yīng)用程序而言,我們可以將視圖注冊給模型,當(dāng)模型數(shù)據(jù)發(fā)生改變時,即時通知視圖頁面發(fā)生改變;而對于Web應(yīng)用而言,即使將多個JSP頁面注冊給一個模型,當(dāng)模型發(fā)生變化時,模型也無法主動發(fā)送消息給JSP頁面(因為Web應(yīng)用都是基于請求/響應(yīng)模式的),只有當(dāng)用戶請求瀏覽該頁面時,控制器才負(fù)責(zé)調(diào)用模型數(shù)據(jù)來更新JSP頁面。
1.2.4 Struts:基于MVC 的堅固框架
Struts是在MVC模式基礎(chǔ)上構(gòu)建Web應(yīng)用程序的一種開放源碼框架。Structs鼓勵在MVC模式上構(gòu)建應(yīng)用程序并且提供大多數(shù)Web應(yīng)用程序所共有的服務(wù)。
在Struts應(yīng)用程序中,可以構(gòu)建模型層,這樣業(yè)務(wù)邏輯與數(shù)據(jù)檢索邏輯重用就很容易了。這層負(fù)責(zé)運(yùn)行應(yīng)用程序的業(yè)務(wù)邏輯,獲取相關(guān)數(shù)據(jù)(如運(yùn)行SQL命令或讀取平面文件)。
Struts鼓勵在模型—視圖—控制器設(shè)計范例基礎(chǔ)上構(gòu)建應(yīng)用程序。Structs提供自己的控制器組件(ActionController類)并與其他技術(shù)相結(jié)合來提供模型與視圖。對于模型(Model類), Struts能與任何標(biāo)準(zhǔn)的數(shù)據(jù)訪問技術(shù)相結(jié)合,包括EJB、JDBC,以及Object-Relational Bridge。對于視圖(ActionForm類),Struts在JSP環(huán)境及其他描述系統(tǒng)中運(yùn)行地很好。
1.Struts 1
從過去的經(jīng)驗來看,Struts 1是所有MVC框架中不容辯駁的勝利者,不管是市場占有率,還是所擁有的開發(fā)人群,Struts 1都擁有其他MVC框架不可比擬的優(yōu)勢。Struts 1的成功得益于它豐富的文檔、活躍的開發(fā)群體。當(dāng)然,Struts 1是世界上第一個發(fā)布的MVC框架,Struts 1.0在2001年6月發(fā)布,這一點可能是使它得到如此廣泛擁戴的主要原因。
為了使讀者明白Struts 1的運(yùn)行機(jī)制,下面將簡要介紹Struts 1的基本框架。
Struts 1框架以ActionServlet作為核心控制器,整個應(yīng)用由客戶端請求驅(qū)動。當(dāng)客戶端向Web應(yīng)用發(fā)送請求時,請求將被Struts 1的核心控制器ActionServlet攔截,ActionServlet根據(jù)請求決定是否需要調(diào)用業(yè)務(wù)邏輯控制器處理用戶請求(實際上,業(yè)務(wù)邏輯控制器還是控制器,它只是負(fù)責(zé)調(diào)用模型來處理用戶請求),當(dāng)用戶請求處理完成后,其處理結(jié)果通過JSP呈現(xiàn)給用戶。
對于整個Struts 1框架而言,控制器就是它的核心,Struts 1的控制器由兩個部分組成:核心控制器和業(yè)務(wù)邏輯控制器。其中核心控制器就是ActionServlet,由Struts 1框架提供;業(yè)務(wù)邏輯控制就是用戶自定義的Action,由應(yīng)用開發(fā)者提供。
大部分用戶請求,都需要得到服務(wù)器的處理。當(dāng)用戶發(fā)送一個需要得到服務(wù)器處理的請求時,該請求被ActionServlet攔截到,ActionServlet將該請求轉(zhuǎn)發(fā)給對應(yīng)的業(yè)務(wù)邏輯控制器,業(yè)務(wù)邏輯控制器調(diào)用模型來處理用戶請求;如果用戶請求只是希望得到某個URL資源,則由ActionServlet將被請求的資源轉(zhuǎn)發(fā)給用戶。
Struts 1的程序運(yùn)行流程如圖1-9所示。

圖1-9 Struts 1的程序運(yùn)行流程
下面針對Struts 1程序流程具體分析MVC中的3個角色。
(1)Model部分。
Struts 1的Model部分主要由底層的業(yè)務(wù)邏輯組件充當(dāng),這些業(yè)務(wù)邏輯組件封裝了底層數(shù)據(jù)庫訪問、業(yè)務(wù)邏輯方法實現(xiàn)。實際上,對于一個成熟的企業(yè)應(yīng)用而言,Model部分也不是一個簡單的JavaBean所能完成的,它可能是一個或多個EJB組件,可能是一個WebService服務(wù)。總之,Model部分封裝了整個應(yīng)用的所有業(yè)務(wù)邏輯,但整個部分并不是由Struts 1提供的,Struts 1也沒有為實現(xiàn)Model組件提供任何支持。
(2)View部分。
Struts 1的View部分采用JSP實現(xiàn)。Struts 1提供了豐富的標(biāo)簽庫,通過這些標(biāo)簽庫可以最大限度地減少腳本的使用。這些自定義的標(biāo)簽庫可以輸出控制器的處理結(jié)果。
雖然Struts 1提供了與Ties框架的整合,但Struts 1所支持的表現(xiàn)層技術(shù)非常單一:既不支持FreeMarker、Velocity等模板技術(shù),也不支持JasperReports等報表技術(shù)。
(3)Controller部分。
Struts 1的Controller部分由兩個部分組成。
■ 系統(tǒng)核心控制器:由Struts 1框架提供,也就是系統(tǒng)中的ActionServlet。
■ 業(yè)務(wù)邏輯控制器:由Struts 1框架提供,也就是用戶自己實現(xiàn)的Action實例。
Struts 1的核心控制器對應(yīng)圖1-9中的核心控制器(ActionServlet)。該控制器由Struts 1框架提供,繼承HttpServlet類,因此可以配置成一個標(biāo)準(zhǔn)的Servlet,該控制器負(fù)責(zé)攔截所有HTTP請求,然后根據(jù)用戶請求決定是否需要調(diào)用業(yè)務(wù)邏輯控制器,如果需要調(diào)用業(yè)務(wù)邏輯控制器,則將請求轉(zhuǎn)發(fā)給Action處理,否則直接轉(zhuǎn)向請求的JSP頁面。
業(yè)務(wù)邏輯控制器負(fù)責(zé)處理用戶請求,但業(yè)務(wù)邏輯控制器本身并不具有處理能力,而是調(diào)用Model來完成處理。
Struts 1提供了系統(tǒng)所需要的核心控制器,也為實現(xiàn)業(yè)務(wù)邏輯控制器提供了許多支持。因此,控制器部分就是Struts 1框架的核心。有時候,我們直接將MVC層稱為控制器層。
提示:對于任何MVC框架而言,其實只實現(xiàn)了C(控制器)部分,但它負(fù)責(zé)用控制器調(diào)用業(yè)務(wù)邏輯組件,并負(fù)責(zé)控制器與視圖技術(shù)(JSP、FreeMarker和Velocity等)的整合。
對于Struts 1框架而言,因為它與JSP/Servlet耦合非常緊密,導(dǎo)致了許多不可避免的缺陷,隨著Web應(yīng)用的逐漸擴(kuò)大,這些缺陷逐漸變成制約Struts 1發(fā)展的重要因素,這也是Struts 2出現(xiàn)的原因。下面具體分析Struts 1中存在的種種缺陷。
(1)支持的表現(xiàn)層技術(shù)單一。
Struts 1只支持JSP作為表現(xiàn)層技術(shù),不提供與其他表現(xiàn)層技術(shù)(如Velocity、FreeMarker等)的整合。這一點嚴(yán)重制約了Struts 1框架的使用,對于目前很多Java EE應(yīng)用而言,并不一定使用JSP作為表現(xiàn)層技術(shù)。
雖然Struts 1處理完用戶請求后,并沒有直接轉(zhuǎn)到特定的視圖資源,而是返回一個ActionForward對象(可以將ActionForward理解為一個邏輯視圖名),在struts-config.xml文件中定義了邏輯視圖名和視圖資源之間的對應(yīng)關(guān)系,ActionServlet得到處理器返回的ActionForword對象后,可以根據(jù)邏輯視圖名和視圖資源之間的對應(yīng)關(guān)系,將視圖資源呈現(xiàn)給用戶。
從上面的設(shè)計來看,不得不佩服Struts 1的設(shè)計者高度解耦的設(shè)計:控制器并沒有直接執(zhí)行轉(zhuǎn)發(fā)請求,而僅僅返回一個邏輯視圖名,實際的轉(zhuǎn)發(fā)放在配置文件中進(jìn)行管理。但因為Struts 1框架出現(xiàn)的年代太早了,那時候還沒有FreeMarker、Velocity等技術(shù),因而沒有考慮與FreeMarker、Velocity等視圖技術(shù)的整合。
提示:Struts 1已經(jīng)通過配置文件管理邏輯視圖名和實際視圖之間的對應(yīng)關(guān)系,只是沒有做到讓邏輯視圖名可以支持更多的視圖技術(shù)。
雖然Struts 1有非常優(yōu)秀的設(shè)計,但由于歷史原因,它沒有提供與更多視圖技術(shù)的整合,這嚴(yán)重限制了Struts 1的使用。
(2)與Servlet API嚴(yán)重耦合,難于測試。
Struts 1框架是在Model 2的基礎(chǔ)上發(fā)展起來的,因此它完全是基于Servlet API的,所以在Struts 1的業(yè)務(wù)邏輯控制器內(nèi),充滿了大量的Servlet API。
(3)代碼嚴(yán)重依賴于Struts 1 API,屬于侵入式設(shè)計。
Struts 1的Action類必須繼承Struts 1的Action基類,實現(xiàn)處理方法時,又包含了大量Struts 1 API,如ActionMapping、ActionForm和ActionForward類。這種侵入式設(shè)計的最大弱點在于,一旦系統(tǒng)需要重構(gòu),這些Action類將完全沒有利用價值。
可見,Struts 1的Action類這種侵入式設(shè)計導(dǎo)致了較低的代碼復(fù)用。
2.WebWork
WebWork雖然沒有Struts 1那樣赫赫有名,但也是出身名門,WebWork來自另外一個優(yōu)秀的開源組織——opensymphony,這個優(yōu)秀的開源組織同樣開發(fā)了大量優(yōu)秀的開源項目,如Qutarz、OSWorkFlow等。實際上,WebWork的創(chuàng)始人則是另一個Java領(lǐng)域的名人——Rickard Oberg(他就是JBoss和XDoclet的作者)。
相對于Struts 1存在的先天性不足而言,WebWork則更加優(yōu)秀,它采用了一種更加松耦合的設(shè)計,讓系統(tǒng)的Action不再與Servlet API耦合。使單元測試更加方便,允許系統(tǒng)從B/S結(jié)構(gòu)向C/S結(jié)構(gòu)轉(zhuǎn)換。相對于Struts 1僅支持JSP表現(xiàn)層技術(shù)的缺陷而言,WebWork支持更多的表現(xiàn)層技術(shù),如Velocity、FreeMarker和XSLT等。
WebWork可以脫離Web應(yīng)用使用,這一點似乎并沒有太多優(yōu)勢,因為,一個應(yīng)用通常在開始時已經(jīng)確定在怎樣的環(huán)境下使用。WebWork有自己的控制反轉(zhuǎn)(Inversion of Control)容器,通過控制反轉(zhuǎn),可以讓測試變得更簡單,通過設(shè)置實現(xiàn)服務(wù)接口的Mock對象完成測試,而不需要設(shè)置服務(wù)注冊。
WebWork框架結(jié)構(gòu)如圖1-10所示。

圖1-10 WebWork框架結(jié)構(gòu)圖
WebWork 2使用OGNL這個強(qiáng)大的表達(dá)式語言,可以訪問值棧。OGNL對集合和索引屬性的支持非常強(qiáng)大。WebWork建立在XWork之上,使用ServletDispatcher作為該框架的核心控制器,處理HTTP的響應(yīng)和請求。
從處理流程上來看,WebWork與Struts 1非常類似,它們的核心都由控制器組成,其中控制器都由兩個部分組成:核心控制器ServletDispatcher,該控制器由框架提供;業(yè)務(wù)邏輯控制器Action,該控制器由程序員提供。相對于Struts 1的Action與Servlet API緊緊耦合的弱點而言,WebWork的Action則完全與Servlet API分離,因此該Action更容易測試。
WebWork的Action可以與Servlet API分離,得益于它靈巧的設(shè)計,它使用一個攔截器鏈,負(fù)責(zé)將用戶請求數(shù)據(jù)轉(zhuǎn)發(fā)到Action,并負(fù)責(zé)將Action的處理結(jié)果轉(zhuǎn)換成對用戶的響應(yīng)。
當(dāng)用戶向Web應(yīng)用發(fā)送請求時,該請求經(jīng)過ActionContextCleanUp、SiteMesh等過濾器過濾,由WebWork的核心控制器攔截,如果用戶請求需要WebWork的業(yè)務(wù)邏輯控制器處理,該控制器則調(diào)用Action映射器,該映射器將用戶請求轉(zhuǎn)發(fā)到對應(yīng)的業(yè)務(wù)邏輯控制器。值得注意的是,此時的業(yè)務(wù)邏輯控制器并不是開發(fā)者實現(xiàn)的控制器,而是WebWork創(chuàng)建的控制器代理。
創(chuàng)建控制器代理時,WebWork需要得到開發(fā)者定義的xwork.xml配置文件,控制器代理以用戶實現(xiàn)的控制器作為目標(biāo),以攔截器鏈中的攔截器作為處理(Advice)。
提示:WebWork中創(chuàng)建控制器代理的方式,就是一種AOP(面向切面編程)編程方式,只是這種AOP中的攔截器由系統(tǒng)提供,因此無需用戶參與。
開發(fā)者自己實現(xiàn)的業(yè)務(wù)邏輯控制器只是WebWork業(yè)務(wù)控制器的目標(biāo)——這就是開發(fā)者自己實現(xiàn)的Action可以與Servlet API分離的原因。當(dāng)開發(fā)者自己的Action處理完HTTP請求后,該結(jié)果只是一個普通字符串,該字符串將對應(yīng)到指定的視圖資源。
指定的視圖資源經(jīng)過攔截器鏈的處理后,生成對客戶端的響應(yīng)輸出。
與前面的Struts 1框架對比,不難發(fā)現(xiàn)WebWork在很多地方確實更優(yōu)秀。相對于Struts 1的種種缺點而言,WebWork存在如下優(yōu)點:
(1)Action無需與Servlet API耦合,更容易測試。
相對于Struts 1框架中的Action出現(xiàn)了大量Servlet API而言,WebWork的Action更像一個普通Java對象,該控制器代碼中沒有耦合任何Servlet API。
(2)Action無需與WebWork耦合,代碼重用率高。
Struts 1中的Action類需要繼承Struts 1的Action類。我們知道,實現(xiàn)一個接口和繼承一個類是完全不同的概念。實現(xiàn)一個接口對類的污染要小得多,該類也可以實現(xiàn)其他任意接口,還可以繼承一個父類;但一旦已經(jīng)繼承一個父類,則意味著該類不能再繼承其他父類。
得益于WebWork靈巧的設(shè)計,WebWork中的Action無需與任何Servlet API、WebWork API耦合,從而具有更好的代碼重用率。
(3)支持更多的表現(xiàn)層技術(shù),有更好的適應(yīng)性。
從圖1-10中可以看到,WebWork對多種表現(xiàn)層技術(shù),如JSP、Velocity和FreeMarker等都有很好的支持,從而給開發(fā)更多的選擇,提供了更好的適應(yīng)性。
3.Struts 2
雖然Struts 2號稱是一個全新的框架,但這僅僅是相對Struts 1而言的。Struts 2與Struts 1相比,確實有很多革命性的改進(jìn),但它并不是新發(fā)布的新框架,而是在另一個赫赫有名的框架——WebWork的基礎(chǔ)上發(fā)展起來的。從某種程度上來講,Struts 2沒有繼承Struts 1的血統(tǒng),而是繼承了WebWork的血統(tǒng)。或者說,WebWork衍生出了Struts 2,而不是Struts 1衍生出了Struts 2。因為Struts 2是WebWork的升級,而不是一個全新的框架,因此其穩(wěn)定性、性能等各方面都有很好的保證,而且它吸收了Struts 1和WebWork兩者的優(yōu)勢,因此,它是一個非常值得期待的框架。
Apache Struts 2是一個優(yōu)雅的、可擴(kuò)展的JAVA EE web框架。框架設(shè)計的目標(biāo)貫穿整個開發(fā)周期,從開發(fā)到發(fā)布,包括維護(hù)的整個過程。Apache Struts 2就是之前的WebWork 2。在經(jīng)歷了幾年的各自發(fā)展后,WebWork和Struts社區(qū)決定合二為一,也就是Struts 2。
經(jīng)過五年多的發(fā)展,Struts 1已經(jīng)成為一個高度成熟的框架,不管是其穩(wěn)定性還是可靠性,都得到了廣泛的證明。但由于它太“老”了,一些設(shè)計上的缺陷成為它的硬傷。面對大量新的MVC框架的蓬勃興起,Struts 1也開始了血液的更新。
目前,Struts已經(jīng)分化成兩個框架:第一個框架就是傳統(tǒng)Struts 1和WebWork結(jié)合后的Struts 2框架。Struts 2雖然是在Struts 1的基礎(chǔ)上發(fā)展起來的,但實質(zhì)上是以WebWork為核心的, Struts 2為傳統(tǒng)Struts 1注入了WebWork的設(shè)計理念,統(tǒng)一了Struts 1和WebWork兩個框架,允許Struts 1和WebWork開發(fā)者同時使用Struts 2框架。
Struts 2非常類似于WebWork框架,而不像Struts 1框架,因為Struts 2是以WebWork為核心,而不是以Struts 1為核心的。正因為這樣,許多WebWork開發(fā)者會發(fā)現(xiàn),從WebWork過渡到Struts 2是一件非常簡單的事情。
當(dāng)然,對于傳統(tǒng)的Struts 1開發(fā)者,Struts 2也提供了很好的向后兼容性,Struts 2可與Struts 1有機(jī)整合,從而保證Struts 1開發(fā)者能平穩(wěn)過渡到Struts 2。
Struts 2的體系與Struts 1體系的差別非常大,因為Struts 2使用了WebWork的設(shè)計核心,而不是使用Struts 1的設(shè)計核心。Struts 2大量使用攔截器來處理用戶請求,從而允許用戶的業(yè)務(wù)邏輯控制器與Servlet API分離。
Struts 2仍是以前端控制器框架為主體的。這意味著:
■ Actions仍然是通過URL觸發(fā)的。
■ 數(shù)據(jù)仍然是通過URL請求參數(shù)和Form參數(shù)傳送到服務(wù)端的。
■ 所有Servlet對象(如request、response和session等)仍在Action可用。
以下是請求處理過程的高層概覽,如圖1-11所示。

圖1-11 Struts 2請求處理過程
整個請求的處理過程可以分為6步:
(1)由框架產(chǎn)生一個請求并進(jìn)行處理。框架根據(jù)請求匹配相應(yīng)的配置,得到使用哪些攔截器、Action類和返回結(jié)果的信息。
(2)請求通過一系列的攔截器。攔截器和攔截器組可以按照不同級別進(jìn)行組合配置來處理請求。它們?yōu)檎埱筇峁└鞣N預(yù)處理和切面處理的應(yīng)用功能。這和Struts使用Jakarta Commons Chain構(gòu)件的RequestProcessor類很相似。
(3)調(diào)用Action。產(chǎn)生一個新的Action對象實例,并提供請求所調(diào)用的處理邏輯的方法。我們在第二部中將對這一步驟進(jìn)行進(jìn)一步討論。Struts 2可以在配置Action時為請求分配其指定的方法。
(4)調(diào)用相應(yīng)的Result。通過匹配處理Action方法之后的返回值,獲取相應(yīng)的Result類,生成并調(diào)用它的實例。處理Result可能產(chǎn)生的結(jié)果之一就是對UI模板(但并非只有一個)進(jìn)行渲染,來產(chǎn)生HTML。如果是這種情況的話,模板中的Struts 2 tags可以直接從Action中獲取要被渲染的值。
(5)請求再次經(jīng)過一系列攔截器處理后返回。Request以與進(jìn)入時相反的方向通過攔截器組,當(dāng)然,可以在這個過程中進(jìn)行回收整理或額外的處理工作。
(6)響應(yīng)被返回給用戶。最后一步是將控制權(quán)交還給Servlet引擎。最常見的結(jié)果是把渲染后的HTML返回給用戶,但返回的也可能是指定的HTTP頭或進(jìn)行HTTP重定向。
Struts 2和Struts 1的差別中,最明顯的就是Struts 2是一個pull-MVC架構(gòu)。這是什么意思呢?從開發(fā)者角度看,就是說需要顯示給用戶的數(shù)據(jù)可以直接從Action中獲取,而不像Struts 1那樣必須把相應(yīng)的Bean存到Page、Request或Session中才能獲取。
4.Struts 1和Struts 2的對比
下面從10個方面對Struts1和Struts 2進(jìn)行比較。
(1)Action類的比較。
■ Struts1要求Action類繼承一個抽象基類。Struts1的一個普遍問題是使用抽象類編程而不是接口。
■ Struts 2 Action類可以實現(xiàn)一個Action接口,也可實現(xiàn)其他接口,使可選和定制的服務(wù)成為可能。Struts 2提供一個ActionSupport基類去實現(xiàn)常用的接口。Action接口不是必需的,任何有execute標(biāo)志的POJO對象都可以用做Struts 2的Action對象。
(2)線程模式的比較。
■ Struts 1 Action是單例模式并且必須是線程安全的,因為僅有Action的一個實例來處理所有的請求。單例策略限制了Struts 1 Action能做的事,并且要在開發(fā)時特別小心。Action資源必須是線程安全的或同步的。
■ Struts 2 Action對象為每一個請求產(chǎn)生一個實例,因此沒有線程安全問題(實際上Servlet容器給每個請求產(chǎn)生許多可丟棄的對象,并且不會導(dǎo)致性能和垃圾回收問題)。
(3)Servlet依賴。
■ Struts1 Action依賴于Servlet API,因為當(dāng)一個Action被調(diào)用時,HttpServletRequest和HttpServletResponse被傳遞給execute方法。
■ Struts 2 Action不依賴于容器,允許Action脫離容器單獨被測試。如果需要,Struts 2 Action仍然可以訪問初始的request和response。但是,其他的元素減少或消除了直接訪問HttpServetRequest和HttpServletResponse的必要性。
(4)可測性。
■ 測試Struts1 Action的一個主要問題是execute方法暴露了servlet API(這使測試要依賴于容器)。一個第三方擴(kuò)展—Struts TestCase,提供了一套Struts 1的模擬對象來進(jìn)行測試。
■ Struts 2 Action可以通過初始化、設(shè)置屬性、調(diào)用方法來測試,“依賴注入”支持也使測試更容易。
(5)捕獲輸入。
■ Struts 1使用ActionForm對象捕獲輸入。所有的ActionForm必須繼承一個基類。因為其他JavaBean不能用做ActionForm,開發(fā)者經(jīng)常創(chuàng)建多余的類捕獲輸入。動態(tài)Bean (DynaBeans)可以作為創(chuàng)建傳統(tǒng)ActionForm的選擇,但是,開發(fā)者可能是在重新描述(創(chuàng)建)已經(jīng)存在的JavaBean(仍然會導(dǎo)致有冗余的JavaBean)。
■ Struts 2直接使用Action屬性作為輸入屬性,消除了對第二個輸入對象的需求。輸入屬性可能是有自己(子)屬性的rich對象類型。Action屬性能夠通過Web頁面上的taglibs訪問。Struts 2也支持ActionForm模式。rich對象類型包括業(yè)務(wù)對象,能夠用做輸入/輸出對象。這種ModelDriven特性簡化了taglib對POJO輸入對象的引用。
(6)表達(dá)式語言。
■ Struts 1整合了JSTL,因此使用JSTL EL。這種EL有基本對象圖遍歷,但是對集合和索引屬性的支持很弱。
■ Struts 2可以使用JSTL,但是也支持一個更強(qiáng)大和靈活的表達(dá)式語言—“Object Graph Notation Language”(OGNL)。
(7)綁定值到頁面(View)。
■ Struts 1使用標(biāo)準(zhǔn)JSP機(jī)制把對象綁定到頁面中來訪問。
■ Struts 2使用“ValueStack”技術(shù),使taglib能夠訪問值而不需要把你的頁面(View)和對象綁定起來。ValueStack策略允許通過一系列名稱相同但類型不同的屬性重用頁面(View)。
(8)類型轉(zhuǎn)換。
■ Struts 1 ActionForm屬性通常都是String類型。Struts 1使用Commons-Beanutils進(jìn)行類型轉(zhuǎn)換。每個類一個轉(zhuǎn)換器,對每一個實例來說是不可配置的。
■ Struts 2使用OGNL進(jìn)行類型轉(zhuǎn)換。提供基本和常用對象的轉(zhuǎn)換器。
(9)校驗。
■ Struts 1支持在ActionForm的validate方法中手動校驗或通過Commons Validator的擴(kuò)展來校驗。同一個類可以有不同的校驗內(nèi)容,但不能校驗子對象。
■ Struts 2支持通過validate方法和XWork校驗框架來進(jìn)行校驗。XWork校驗框架使用為屬性類類型定義的校驗和內(nèi)容校驗,來支持chain校驗子屬性。
(10)Action執(zhí)行的控制。
■ Struts 1支持每一個模塊有單獨的Request Processors(生命周期),但是模塊中的所有Action必須共享相同的生命周期。
■ Struts 2支持通過攔截器堆棧(Interceptor Stacks)為每一個Action創(chuàng)建不同的生命周期。堆棧能夠根據(jù)需要和不同的Action一起使用。
- Java程序設(shè)計與開發(fā)
- Modular Programming with Python
- Arduino by Example
- Scala Design Patterns
- 算法基礎(chǔ):打開程序設(shè)計之門
- Java技術(shù)手冊(原書第7版)
- Java面向?qū)ο蟪绦蜷_發(fā)及實戰(zhàn)
- Java虛擬機(jī)字節(jié)碼:從入門到實戰(zhàn)
- 網(wǎng)絡(luò)爬蟲原理與實踐:基于C#語言
- Learning ArcGIS for Desktop
- Python Web數(shù)據(jù)分析可視化:基于Django框架的開發(fā)實戰(zhàn)
- Android開發(fā):從0到1 (清華開發(fā)者書庫)
- QGIS 2 Cookbook
- 實戰(zhàn)Java高并發(fā)程序設(shè)計(第2版)
- Visual Basic程序設(shè)計全程指南