一、 什么是Struts
框架(Framework)是可重用的,半完成的應用程序,可以用來產生專門的定制程序。
您只要細心地研究真實的應用程序,就會發現程序大致上由兩類性質不同的組件組成,一類與程序要處理的具體事務密切相關,我們不妨把它們叫做業務組件;另一類是應用服務。比如說:一個稅務征管系統和一個圖書管理系統會在處理它們的業務方面存在很大的差異,這些直接處理業務的組件由于業務性質的不同不大可能在不同的系統中重用,而另一些組件如決定程序流向的控制、輸入的校驗、錯誤處理及標簽庫等這些只與程序相關的組件在不同的系統中可以很好地得到重用。人們自然會想要是把這些在不同應用程序中有共性的一些東西抽取出來,做成一個半成品程序,這樣的半成品就是所謂的程序框架,再做一個新的東西時就不必白手起家,而是可以在這個基礎上開始搭建。實際上,有些大型軟件企業選擇自己搭建這樣的框架。但大多數中小型軟件企業或者其他組織,沒有條件自己建立框架。
Struts作為一個開放原代碼的應用框架,在最近幾年得到了飛速的發展,在JSP Web應用開發中應用得非常廣泛,有的文獻上說它已經成為JSP Web應用框架的事實上的標準。那么,究竟什么是Struts呢?
要回答這個問題還得從JSP Web應用的兩種基本的結構模式:Model 1和Model 2說起,為了給讀者一些實實在在的幫助,并力圖讓學習曲線變得平坦一些,我想采用實例驅動的方法來逐步深入地回答有關問題,因為,學一門技術的最好方法莫過于在實踐中學習、在實踐中體會,逐步加深對其精神實質的理解和把握,而不是一上來就引入一大堆新概念讓大家覺得無所適從,或者死記硬背一大堆概念而面對一個真正的實際需求束手無策。正如,一個人即使在書本上學成了游泳博士,只要他不下水,我想他也是不大可能真正會游泳的。
Model 1結構如圖1所示:
圖1
mode1 1是一個以JSP文件為中心的模式,在這種模式中JSP頁面不僅負責表現邏輯,也負責控制邏輯。專業書籍上稱之為邏輯耦合在頁面中,這種處理方式,對一些規模很小的項目如:一個簡單的留言簿,也沒什么太大的壞處,實際上,人們開始接觸一些對自己來說是新的東西的時候,比如,用JSP訪問數據庫時,往往喜歡別人能提供一個包含這一切的單個JSP頁面,因為這樣在一個頁面上他就可以把握全局,便于理解。但是,用Model 1模式開發大型時,程序流向由一些互相能夠感知的頁面決定,當頁面很多時要清楚地把握其流向將是很復雜的事情,當您修改一頁時可能會影響相關的很多頁面,大有牽一發而動全身的感覺,使得程序的修改與維護變得異常困難;還有一個問題就是程序邏輯開發與頁面設計糾纏在一起,既不便于分工合作也不利于代碼的重用,這樣的程序其健壯性和可伸縮性都不好。
Grady Booch等人在UML用戶指南一書中,強調建模的重要性時,打了一個制作狗窩、私人住宅、和大廈的形象比喻來說明人們處理不同規模的事物時應該采用的合理方法一樣,人們對不同規模的應用程序也應該采用不同的模式。
為了克服Model 1的缺陷,人們引入了Model 2,如圖2所示:
圖2
它引入了"控制器"這個概念,控制器一般由servlet來擔任,客戶端的請求不再直接送給一個處理業務邏輯的JSP頁面,而是送給這個控制器,再由控制器根據具體的請求調用不同的事務邏輯,并將處理結果返回到合適的頁面。因此,這個servlet控制器為應用程序提供了一個進行前-后端處理的中樞。一方面為輸入數據的驗證、身份認證、日志及實現國際化編程提供了一個合適的切入點;另一方面也提供了將業務邏輯從JSP文件剝離的可能。業務邏輯從JSP頁面分離后,JSP文件蛻變成一個單純完成顯示任務的東西,這就是常說的View。而獨立出來的事務邏輯變成人們常說的Model,再加上控制器Control本身,就構成了MVC模式。實踐證明,MVC模式為大型程序的開發及維護提供了巨大的便利。
其實,MVC開始并不是為Web應用程序提出的模式,傳統的MVC要求M將其狀態變化通報給V,但由于Web瀏覽器工作在典型的拉模式而非推模式,很難做到這一點。因此有些人又將用于Web應用的MVC稱之為MVC2。正如上面所提到的MVC是一種模式,當然可以有各種不同的具體實現,包括您自己就可以實現一個體現MVC思想的程序框架,Struts就是一種具體實現MVC2的程序框架。它的大致結構如圖三所示:
圖三
圖三基本勾勒出了一個基于Struts的應用程序的結構,從左到右,分別是其表示層(view)、控制層(controller)、和模型層(Model)。其表示層使用Struts標簽庫構建。來自客戶的所有需要通過框架的請求統一由叫ActionServlet的servlet接收(ActionServlet Struts已經為我們寫好了,只要您應用沒有什么特別的要求,它基本上都能滿足您的要求),根據接收的請求參數和Struts配置(struts-config.xml)中ActionMapping,將請求送給合適的Action去處理,解決由誰做的問題,它們共同構成Struts的控制器。Action則是Struts應用中真正干活的組件,開發人員一般都要在這里耗費大量的時間,它解決的是做什么的問題,它通過調用需要的業務組件(模型)來完成應用的業務,業務組件解決的是如何做的問題,并將執行的結果返回一個代表所需的描繪響應的JSP(或Action)的ActionForward對象給ActionServlet以將響應呈現給客戶。
過程如圖四所示:
圖四
這里要特別說明一下的是:就是Action這個類,上面已經說到了它是Struts中真正干活的地方,也是值得我們高度關注的地方。可是,關于它到底是屬于控制層還是屬于模型層,存在兩種不同的意見,一種認為它屬于模型層,如:《JSP Web編程指南》;另一些則認為它屬于控制層如:《Programming Jakarta Struts》、《Mastering Jakarta Struts》和《Struts Kick Start》等認為它是控制器的一部分,還有其他一些書如《Struts in Action》也建議要避免將業務邏輯放在Action類中,也就是說,圖3中Action后的括號中的內容應該從中移出,但實際中確有一些系統將比較簡單的且不打算重用的業務邏輯放在Action中,所以在圖中還是這樣表示。顯然,將業務對象從Action分離出來后有利于它的重用,同時也增強了應用程序的健壯性和設計的靈活性。因此,它實際上可以看作是Controller與Model的適配器,如果硬要把它歸于那一部分,筆者更傾向于后一種看法,即它是Controller的一部分,換句話說,它不應該包含過多的業務邏輯,而應該只是簡單地收集業務方法所需要的數據并傳遞給業務對象。實際上,它的主要職責是:
校驗前提條件或者聲明
調用需要的業務邏輯方法
檢測或處理其他錯誤
路由控制到相關視圖
上面這樣簡單的描述,初學者可能會感到有些難以接受,下面舉個比較具體的例子來進一步幫助我們理解。如:假設,我們做的是個電子商務程序,現在程序要完成的操作任務是提交定單并返回定單號給客戶,這就是關于做什么的問題,應該由Action類完成,但具體怎么獲得數據庫連接,插入定單數據到數據庫表中,又怎么從數據庫表中取得這個定單號(一般是自增數據列的數據),這一系列復雜的問題,這都是解決怎么做的問題,則應該由一個(假設名為orderBo)業務對象即Model來完成。orderBo可能用一個返回整型值的名為submitOrder的方法來做這件事,Action則是先校驗定單數據是否正確,以免常說的垃圾進垃圾出;如果正確則簡單地調用orderBo的submitOrder方法來得到定單號;它還要處理在調用過程中可能出現任何錯誤;最后根據不同的情況返回不同的結果給客戶。
二、為什么要使用Struts框架
既然本文的開始就說了,自己可以建這種框架,為什么要使用Struts呢?我想下面列舉的這些理由是顯而易見的:首先,它是建立在MVC這種公認的好的模式上的,Struts在M、V和C上都有涉及,但它主要是提供一個好的控制器和一套定制的標簽庫上,也就是說它的著力點在C和V上,因此,它天生就有MVC所帶來的一系列優點,如:結構層次分明,高可重用性,增加了程序的健壯性和可伸縮性,便于開發與設計分工,提供集中統一的權限控制、校驗、國際化、日志等等;其次,它是個開源項目得到了包括它的發明者Craig R.McClanahan在內的一些程序大師和高手持續而細心的呵護,并且經受了實戰的檢驗,使其功能越來越強大,體系也日臻完善;最后,是它對其他技術和框架顯示出很好的融合性。如,現在,它已經與tiles融為一體,可以展望,它很快就會與JSF等融會在一起。當然,和其他任何技術一樣,它也不是十全十美的,如:它對類和一些屬性、參數的命名顯得有些隨意,給使用帶來一些不便;還有如Action類execute方法的只能接收一個ActionForm參數等。但瑕不掩瑜,這些沒有影響它被廣泛使用。
三、Struts的安裝與基本配置
我們主要針對Struts1.1版本進行講解,這里假定讀者已經配置好java運行環境和相應的Web容器,本文例子所使用的是j2sdk和Tomcat4.1.27。下面,將采用類似于step by step的方式介紹其基礎部分。
安裝Struts
到http://jakarta.apache.org/ 下載Struts的安裝文件,本文例子使用的是1.1版。
接下來您要進行如下幾個步驟來完成安裝:
1、解壓下載的安裝文件到您的本地硬盤
2、生成一個新的Web應用,假設我們生成的應用程序的根目錄在/Webapps/mystruts目錄。在server.xml文件中為該應用新建一個別名如/mystruts
3、從第1步解壓的文件中拷貝下列jar文件到/Webapps/mystruts/WEB-INF/lib目錄,主要文件有如下一些。
4、創建一個web.xml文件,這是一個基于servlet的Web應用程序都需要的部署描述文件,一個Struts Web應用,在本質上也是一個基于servlet的Web應用,它也不能例外。
Struts有兩個組件要在該文件中進行配置,它們是:ActionServlet和標簽庫。下面是一個配置清單:
上面我們在web.xml中完成了對servlet和標簽庫的基本配置,而更多的框架組件要在struts-config.xml中進行配置:
5、創建一個基本的struts-config.xml文件,并把它放在/Webapps/mystruts/WEB-INF/目錄中,該文件是基于Struts應用程序的配置描述文件,它將MVC結構中的各組件結合在一起,開發的過程中會不斷對它進行充實和更改。在Struts1.0時,一個應用只能有一個這樣的文件,給分工開發帶來了一些不便,在Struts1.1時,可以有多個這樣的文件,將上述缺點克服了。需在該文件中配置的組件有:data-sources
配置清單如下:
到此為止,我們已經具備了完成一個最簡單Struts應用的所需的各種組件。前面已經提到,在開發過程中我們會不斷充實和修改上面兩個配置描述文件。下面我們將實際做一個非常簡單的應用程序來體驗一下Struts應用開發的真實過程,以期對其有一個真實的認識。在完成基礎部分的介紹后,筆者會給出一些在實際開發中經常用到而又讓初學者感到有些難度的實例。最后,會介紹Struts與其他框架的關系及結合它們生成應用程序的例子。
下面,我們就從一個最簡單的登錄例子入手,以對Struts的主要部分有一些直觀而清晰的認識。這個例子功能非常簡單,假設有一個名為lhb的用戶,其密碼是awave,程序要完成的任務是,呈現一個登錄界面給用戶,如果用戶輸入的名稱和密碼都正確返回一個歡迎頁面給用戶,否則,就返回登錄頁面要求用戶重新登錄并顯示相應的出錯信息。這個例子在我們講述Struts的基礎部分時會反復用到。之所以選用這個簡單的程序作為例子是因為不想讓過于復雜的業務邏輯來沖淡我們的主題。
因為Struts是建立在MVC設計模式上的框架,你可以遵從標準的開發步驟來開發你的Struts Web應用程序,這些步驟大致可以描述如下:
1定義并生成所有代表應用程序的用戶接口的Views,同時生成這些Views所用到的所有ActionForms并將它們添加到struts-config.xml文件中。
2在ApplicationResource.properties文件中添加必要的MessageResources項目
3生成應用程序的控制器。
4在struts-config.xml文件中定義Views與 Controller的關系。
5生成應用程序所需要的model組件
6編譯、運行你的應用程序.
下面,我們就一步步按照上面所說的步驟來完成我們的應用程序:
第一步,我們的應用程序的Views部分包含兩個.jsp頁面:一個是登錄頁面logon.jsp,另一個是用戶登錄成功后的用戶功能頁main.jsp,暫時這個頁面只是個簡單的歡迎頁面。
其中,logon.jsp的代碼清單如下:
main.jsp的代碼清單如下:
首先,我們看一下logon.jsp文件,會發現它有這么兩個鮮明的特點:一是文件頭部有諸如:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
這樣的指令代碼,他們的作用就是指示頁面要用到struts的自定義標簽,標簽庫uri是一個邏輯引用,標簽庫的描述符(tld)的位置在web.xml文件中給出,見上篇文章的配置部分。struts的標簽庫主要由四組標簽組成,它們分別是:
bean標簽,作用是在jsp中操縱bean
logic標簽,作用是在jsp中進行流程控制
html標簽,作用是顯示表單等組件
template標簽,作用是生成動態模板
關于每類標簽的具體作用及語法,因受篇幅限制,不在這里詳細討論,大家可參考struts手冊之類的資料。只是心里要明白所謂標簽其后面的東西就是一些類,這點與bean有些相似,它們在后端運行,生成標準的html標簽返回給瀏覽器。
要使用它們顯然要把它們的標簽庫描述文件引入到我們的系統中,這是些以.tld為擴展名的文件,我們要把它們放在/webapps/mystruts/WEB-INF/目錄下。引入struts標簽后原來普通的html標簽如文本框的標簽變成了這樣的形式 。
Jsp文件的第二個特點,就是頁面上根本沒有直接寫用于顯示的文字如:username,password等東西,而是用 這種形式出現。這個特點為國際化編程打下了堅實的基礎,關于國際化編程后面的文章還會專門討論。
這個簡單的應用所用到的ActionForm為UserInfoForm,代碼清單如下:
在你的應用程序的WEB-INF目錄下再建一個classes目錄,在新建的這個classes目錄下再建如下幾個目錄entity(用于存放ActionForm類)、action目錄(用于存放Action類)、bussness目錄(用于存放作為Model的業務對象類)。Classes目錄下的子目錄就是所謂的包,以后,還會根據需要增加相應的包。
現在,將UserInfoForm.java保存到entity目錄中。
把如下代碼添加到/webapps/mystruts/WEB-INF/struts-config.xml文件中
特別要提醒一下的是:關于ActionForm的大小寫,一定要按照上面的寫,以免造成不必要的麻煩。
到此,我們完成了第一步工作。
第二步,我們建一個名為ApplicationResource.properties的文件,并把它放在/webapps/mystruts/WEB-INF/classes目錄下。它在struts-config.xml的配置信息我們已在第一篇文章的末尾說了,就是:
目前我們在ApplicationResource.properties文件中加入的內容是:
到此,我們已完成了第二個步驟。
第三步,我們開始生成和配置Controller組件。
在前面我們已經提到,Struts應用程序的控制器由org.apache.struts.action.ActionServlet和org.apache.struts.action.Action類組成,其中,前者已由Struts準備好了,后者Struts只是為我們提供了個骨架,我們要做的是為實現應用程序的特定功能而擴展Action類,下面是實現我們登錄程序的Action類的代碼清單:
這個action類中有兩個錯誤消息鍵要加到ApplicationResource.properties文件中,清單如下:
第四步:在struts-config.xml文件中定義Views與 Controller的關系,也就是配置所謂的ActionMapping。它們在struts-config.xml中的位置是排在… 標簽后,我們的登錄程序的配置清單如下:
第五步:生成應用程序所需要的model組件,該組件是完成應用程序業務邏輯的地方,現在我的登錄程序的業務邏輯很簡單,就是判斷用戶是不是lhb并且其口令是不是awave如果是就返回一個表示匹配的字符串"match",否則,就拋出出錯信息。其代碼清單如下:
將其放在bussness包中。
我們同樣要將其表示錯誤信息的鍵值設置在ApplicationResource.properties文件中,清單如下:
到此為止,我們已經完成了這個簡單登錄程序的所有組件。下面就可以享受我們的勞動成果了。
第六步、編譯運行應用程序。
常規的做法是用Ant來裝配和部署Struts應用程序,如果按這個套路,這篇文章就會顯得十分冗長乏味,同時也沒有太大的必要,因為,用一個IDE一般可以很方便地生成一個應用。因此,我們采用簡便的方法,直接編譯我們的.java文件。不過這里要注意一點的是:實踐證明,要使得編譯過程不出錯,還必須將struts.jar文件放一份拷貝到/common/lib目錄中,并在環境變量中設置CLASSPATH 其值是/common/lib/struts.jar;配置好后就可以分別編譯entity、bussness及action目錄下的.java文件了。編譯完成后:打開/conf目錄下的server.xml文件,在前加上如下語句為我們的應用程序建一個虛擬目錄:
啟動,tomcat。在瀏覽器中輸入:http://localhost:8080/mystruts/logon.jsp
如果前面的步驟沒有紕漏的話,一個如圖所示的登錄畫面就會出現在你的眼前。
如果,不輸入任何內容直接點擊Submit按鈕,就會返回到logon.jsp并顯示missing username和missing password錯誤信息;如果輸入其他內容,則會返回no matched user的錯誤;如果輸入的用戶名是lhb且口令是awave則會顯示表示登錄成功的歡迎頁面。
上面雖然是一個功能很簡單的應用程序,但麻雀雖小,五臟俱全,基本涉及到了struts的主要組成部分。下面我們就來分析一下程序的特點和基本的工作原理。
首先,我們在瀏覽器中輸入.jsp文件時,后臺將struts的自定義標簽"翻譯"成普通的html標簽返回給瀏覽器,而一些提示信息如作為輸入框label的username、password還有按鈕上提示信息還有錯誤信息等都來自MessageResources即ApplicationResource.properties文件中對應的鍵值。當我們點擊Submit按鈕時,從web.xml的配置可以看出,請求將被ActionServlet截獲。它通過表單中提供的action參數在struts-config.xml文件中查找對應的項目,如果有對應的ActionForm,它就用表單中數據填充ActionForm的對應屬性,本例中的ActionForm為userInfoForm,相應的屬性是username和password,這就是所謂的實例化ActionForm。然后,將控制交給對應的Action,本例中是LogonAction,它做的主要工作是對ActionForm中取出的username和password做了一下校驗,這里只是簡單檢驗它們是否為空(這些簡單的格式化方面的校驗應該放在客戶端進行,而且struts也為我們提供了一個很好的模式,后面如果有可能會詳細介紹)。如果不為空則調用判斷用戶及口令是否正確的業務邏輯模塊UserInfoBo,同時,它會捕獲可能出現的錯誤,然后根據業務邏輯返回的結果將程序導向不同的頁面,本例中如果業務邏輯返回的結果是"match"則依據中的 返回main.jsp頁面給瀏覽器同時在session對象中保存了用戶的登錄信息;否則,返回輸入頁面并顯示相應的出錯信息,完成了上篇文章所說的它的四個主要職責。
大家一定注意到了,在本例的業務邏輯模塊UserInfoBo中,將用戶與密碼是寫死在程序中的,在一個真實的應用程序中是不會這樣做的,那些需要永久保存的信息如,username及口令等都會保存在數據庫文件之類的永久介質中,下一篇文章我們將介紹在struts中如何訪問數據庫。
框架(Framework)是可重用的,半完成的應用程序,可以用來產生專門的定制程序。
您只要細心地研究真實的應用程序,就會發現程序大致上由兩類性質不同的組件組成,一類與程序要處理的具體事務密切相關,我們不妨把它們叫做業務組件;另一類是應用服務。比如說:一個稅務征管系統和一個圖書管理系統會在處理它們的業務方面存在很大的差異,這些直接處理業務的組件由于業務性質的不同不大可能在不同的系統中重用,而另一些組件如決定程序流向的控制、輸入的校驗、錯誤處理及標簽庫等這些只與程序相關的組件在不同的系統中可以很好地得到重用。人們自然會想要是把這些在不同應用程序中有共性的一些東西抽取出來,做成一個半成品程序,這樣的半成品就是所謂的程序框架,再做一個新的東西時就不必白手起家,而是可以在這個基礎上開始搭建。實際上,有些大型軟件企業選擇自己搭建這樣的框架。但大多數中小型軟件企業或者其他組織,沒有條件自己建立框架。
Struts作為一個開放原代碼的應用框架,在最近幾年得到了飛速的發展,在JSP Web應用開發中應用得非常廣泛,有的文獻上說它已經成為JSP Web應用框架的事實上的標準。那么,究竟什么是Struts呢?
要回答這個問題還得從JSP Web應用的兩種基本的結構模式:Model 1和Model 2說起,為了給讀者一些實實在在的幫助,并力圖讓學習曲線變得平坦一些,我想采用實例驅動的方法來逐步深入地回答有關問題,因為,學一門技術的最好方法莫過于在實踐中學習、在實踐中體會,逐步加深對其精神實質的理解和把握,而不是一上來就引入一大堆新概念讓大家覺得無所適從,或者死記硬背一大堆概念而面對一個真正的實際需求束手無策。正如,一個人即使在書本上學成了游泳博士,只要他不下水,我想他也是不大可能真正會游泳的。
Model 1結構如圖1所示:

圖1
mode1 1是一個以JSP文件為中心的模式,在這種模式中JSP頁面不僅負責表現邏輯,也負責控制邏輯。專業書籍上稱之為邏輯耦合在頁面中,這種處理方式,對一些規模很小的項目如:一個簡單的留言簿,也沒什么太大的壞處,實際上,人們開始接觸一些對自己來說是新的東西的時候,比如,用JSP訪問數據庫時,往往喜歡別人能提供一個包含這一切的單個JSP頁面,因為這樣在一個頁面上他就可以把握全局,便于理解。但是,用Model 1模式開發大型時,程序流向由一些互相能夠感知的頁面決定,當頁面很多時要清楚地把握其流向將是很復雜的事情,當您修改一頁時可能會影響相關的很多頁面,大有牽一發而動全身的感覺,使得程序的修改與維護變得異常困難;還有一個問題就是程序邏輯開發與頁面設計糾纏在一起,既不便于分工合作也不利于代碼的重用,這樣的程序其健壯性和可伸縮性都不好。
Grady Booch等人在UML用戶指南一書中,強調建模的重要性時,打了一個制作狗窩、私人住宅、和大廈的形象比喻來說明人們處理不同規模的事物時應該采用的合理方法一樣,人們對不同規模的應用程序也應該采用不同的模式。
為了克服Model 1的缺陷,人們引入了Model 2,如圖2所示:

圖2
它引入了"控制器"這個概念,控制器一般由servlet來擔任,客戶端的請求不再直接送給一個處理業務邏輯的JSP頁面,而是送給這個控制器,再由控制器根據具體的請求調用不同的事務邏輯,并將處理結果返回到合適的頁面。因此,這個servlet控制器為應用程序提供了一個進行前-后端處理的中樞。一方面為輸入數據的驗證、身份認證、日志及實現國際化編程提供了一個合適的切入點;另一方面也提供了將業務邏輯從JSP文件剝離的可能。業務邏輯從JSP頁面分離后,JSP文件蛻變成一個單純完成顯示任務的東西,這就是常說的View。而獨立出來的事務邏輯變成人們常說的Model,再加上控制器Control本身,就構成了MVC模式。實踐證明,MVC模式為大型程序的開發及維護提供了巨大的便利。
其實,MVC開始并不是為Web應用程序提出的模式,傳統的MVC要求M將其狀態變化通報給V,但由于Web瀏覽器工作在典型的拉模式而非推模式,很難做到這一點。因此有些人又將用于Web應用的MVC稱之為MVC2。正如上面所提到的MVC是一種模式,當然可以有各種不同的具體實現,包括您自己就可以實現一個體現MVC思想的程序框架,Struts就是一種具體實現MVC2的程序框架。它的大致結構如圖三所示:

圖三
圖三基本勾勒出了一個基于Struts的應用程序的結構,從左到右,分別是其表示層(view)、控制層(controller)、和模型層(Model)。其表示層使用Struts標簽庫構建。來自客戶的所有需要通過框架的請求統一由叫ActionServlet的servlet接收(ActionServlet Struts已經為我們寫好了,只要您應用沒有什么特別的要求,它基本上都能滿足您的要求),根據接收的請求參數和Struts配置(struts-config.xml)中ActionMapping,將請求送給合適的Action去處理,解決由誰做的問題,它們共同構成Struts的控制器。Action則是Struts應用中真正干活的組件,開發人員一般都要在這里耗費大量的時間,它解決的是做什么的問題,它通過調用需要的業務組件(模型)來完成應用的業務,業務組件解決的是如何做的問題,并將執行的結果返回一個代表所需的描繪響應的JSP(或Action)的ActionForward對象給ActionServlet以將響應呈現給客戶。
過程如圖四所示:

圖四
這里要特別說明一下的是:就是Action這個類,上面已經說到了它是Struts中真正干活的地方,也是值得我們高度關注的地方。可是,關于它到底是屬于控制層還是屬于模型層,存在兩種不同的意見,一種認為它屬于模型層,如:《JSP Web編程指南》;另一些則認為它屬于控制層如:《Programming Jakarta Struts》、《Mastering Jakarta Struts》和《Struts Kick Start》等認為它是控制器的一部分,還有其他一些書如《Struts in Action》也建議要避免將業務邏輯放在Action類中,也就是說,圖3中Action后的括號中的內容應該從中移出,但實際中確有一些系統將比較簡單的且不打算重用的業務邏輯放在Action中,所以在圖中還是這樣表示。顯然,將業務對象從Action分離出來后有利于它的重用,同時也增強了應用程序的健壯性和設計的靈活性。因此,它實際上可以看作是Controller與Model的適配器,如果硬要把它歸于那一部分,筆者更傾向于后一種看法,即它是Controller的一部分,換句話說,它不應該包含過多的業務邏輯,而應該只是簡單地收集業務方法所需要的數據并傳遞給業務對象。實際上,它的主要職責是:
上面這樣簡單的描述,初學者可能會感到有些難以接受,下面舉個比較具體的例子來進一步幫助我們理解。如:假設,我們做的是個電子商務程序,現在程序要完成的操作任務是提交定單并返回定單號給客戶,這就是關于做什么的問題,應該由Action類完成,但具體怎么獲得數據庫連接,插入定單數據到數據庫表中,又怎么從數據庫表中取得這個定單號(一般是自增數據列的數據),這一系列復雜的問題,這都是解決怎么做的問題,則應該由一個(假設名為orderBo)業務對象即Model來完成。orderBo可能用一個返回整型值的名為submitOrder的方法來做這件事,Action則是先校驗定單數據是否正確,以免常說的垃圾進垃圾出;如果正確則簡單地調用orderBo的submitOrder方法來得到定單號;它還要處理在調用過程中可能出現任何錯誤;最后根據不同的情況返回不同的結果給客戶。
二、為什么要使用Struts框架
既然本文的開始就說了,自己可以建這種框架,為什么要使用Struts呢?我想下面列舉的這些理由是顯而易見的:首先,它是建立在MVC這種公認的好的模式上的,Struts在M、V和C上都有涉及,但它主要是提供一個好的控制器和一套定制的標簽庫上,也就是說它的著力點在C和V上,因此,它天生就有MVC所帶來的一系列優點,如:結構層次分明,高可重用性,增加了程序的健壯性和可伸縮性,便于開發與設計分工,提供集中統一的權限控制、校驗、國際化、日志等等;其次,它是個開源項目得到了包括它的發明者Craig R.McClanahan在內的一些程序大師和高手持續而細心的呵護,并且經受了實戰的檢驗,使其功能越來越強大,體系也日臻完善;最后,是它對其他技術和框架顯示出很好的融合性。如,現在,它已經與tiles融為一體,可以展望,它很快就會與JSF等融會在一起。當然,和其他任何技術一樣,它也不是十全十美的,如:它對類和一些屬性、參數的命名顯得有些隨意,給使用帶來一些不便;還有如Action類execute方法的只能接收一個ActionForm參數等。但瑕不掩瑜,這些沒有影響它被廣泛使用。
三、Struts的安裝與基本配置
我們主要針對Struts1.1版本進行講解,這里假定讀者已經配置好java運行環境和相應的Web容器,本文例子所使用的是j2sdk和Tomcat4.1.27。下面,將采用類似于step by step的方式介紹其基礎部分。
安裝Struts
到http://jakarta.apache.org/ 下載Struts的安裝文件,本文例子使用的是1.1版。
接下來您要進行如下幾個步驟來完成安裝:
1、解壓下載的安裝文件到您的本地硬盤
2、生成一個新的Web應用,假設我們生成的應用程序的根目錄在
3、從第1步解壓的文件中拷貝下列jar文件到
|
4、創建一個web.xml文件,這是一個基于servlet的Web應用程序都需要的部署描述文件,一個Struts Web應用,在本質上也是一個基于servlet的Web應用,它也不能例外。
Struts有兩個組件要在該文件中進行配置,它們是:ActionServlet和標簽庫。下面是一個配置清單:
|
上面我們在web.xml中完成了對servlet和標簽庫的基本配置,而更多的框架組件要在struts-config.xml中進行配置:
5、創建一個基本的struts-config.xml文件,并把它放在
|
配置清單如下:
|
到此為止,我們已經具備了完成一個最簡單Struts應用的所需的各種組件。前面已經提到,在開發過程中我們會不斷充實和修改上面兩個配置描述文件。下面我們將實際做一個非常簡單的應用程序來體驗一下Struts應用開發的真實過程,以期對其有一個真實的認識。在完成基礎部分的介紹后,筆者會給出一些在實際開發中經常用到而又讓初學者感到有些難度的實例。最后,會介紹Struts與其他框架的關系及結合它們生成應用程序的例子。
下面,我們就從一個最簡單的登錄例子入手,以對Struts的主要部分有一些直觀而清晰的認識。這個例子功能非常簡單,假設有一個名為lhb的用戶,其密碼是awave,程序要完成的任務是,呈現一個登錄界面給用戶,如果用戶輸入的名稱和密碼都正確返回一個歡迎頁面給用戶,否則,就返回登錄頁面要求用戶重新登錄并顯示相應的出錯信息。這個例子在我們講述Struts的基礎部分時會反復用到。之所以選用這個簡單的程序作為例子是因為不想讓過于復雜的業務邏輯來沖淡我們的主題。
因為Struts是建立在MVC設計模式上的框架,你可以遵從標準的開發步驟來開發你的Struts Web應用程序,這些步驟大致可以描述如下:
1定義并生成所有代表應用程序的用戶接口的Views,同時生成這些Views所用到的所有ActionForms并將它們添加到struts-config.xml文件中。
2在ApplicationResource.properties文件中添加必要的MessageResources項目
3生成應用程序的控制器。
4在struts-config.xml文件中定義Views與 Controller的關系。
5生成應用程序所需要的model組件
6編譯、運行你的應用程序.
下面,我們就一步步按照上面所說的步驟來完成我們的應用程序:
第一步,我們的應用程序的Views部分包含兩個.jsp頁面:一個是登錄頁面logon.jsp,另一個是用戶登錄成功后的用戶功能頁main.jsp,暫時這個頁面只是個簡單的歡迎頁面。
其中,logon.jsp的代碼清單如下:
|
main.jsp的代碼清單如下:
|
首先,我們看一下logon.jsp文件,會發現它有這么兩個鮮明的特點:一是文件頭部有諸如:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
這樣的指令代碼,他們的作用就是指示頁面要用到struts的自定義標簽,標簽庫uri是一個邏輯引用,標簽庫的描述符(tld)的位置在web.xml文件中給出,見上篇文章的配置部分。struts的標簽庫主要由四組標簽組成,它們分別是:
關于每類標簽的具體作用及語法,因受篇幅限制,不在這里詳細討論,大家可參考struts手冊之類的資料。只是心里要明白所謂標簽其后面的東西就是一些類,這點與bean有些相似,它們在后端運行,生成標準的html標簽返回給瀏覽器。
要使用它們顯然要把它們的標簽庫描述文件引入到我們的系統中,這是些以.tld為擴展名的文件,我們要把它們放在
Jsp文件的第二個特點,就是頁面上根本沒有直接寫用于顯示的文字如:username,password等東西,而是用
這個簡單的應用所用到的ActionForm為UserInfoForm,代碼清單如下:
|
在你的應用程序的WEB-INF目錄下再建一個classes目錄,在新建的這個classes目錄下再建如下幾個目錄entity(用于存放ActionForm類)、action目錄(用于存放Action類)、bussness目錄(用于存放作為Model的業務對象類)。Classes目錄下的子目錄就是所謂的包,以后,還會根據需要增加相應的包。
現在,將UserInfoForm.java保存到entity目錄中。
把如下代碼添加到
|
特別要提醒一下的是:關于ActionForm的大小寫,一定要按照上面的寫,以免造成不必要的麻煩。
到此,我們完成了第一步工作。
第二步,我們建一個名為ApplicationResource.properties的文件,并把它放在
目前我們在ApplicationResource.properties文件中加入的內容是:
|
到此,我們已完成了第二個步驟。
第三步,我們開始生成和配置Controller組件。
在前面我們已經提到,Struts應用程序的控制器由org.apache.struts.action.ActionServlet和org.apache.struts.action.Action類組成,其中,前者已由Struts準備好了,后者Struts只是為我們提供了個骨架,我們要做的是為實現應用程序的特定功能而擴展Action類,下面是實現我們登錄程序的Action類的代碼清單:
|
這個action類中有兩個錯誤消息鍵要加到ApplicationResource.properties文件中,清單如下:
|
第四步:在struts-config.xml文件中定義Views與 Controller的關系,也就是配置所謂的ActionMapping。它們在struts-config.xml中的位置是排在
|
第五步:生成應用程序所需要的model組件,該組件是完成應用程序業務邏輯的地方,現在我的登錄程序的業務邏輯很簡單,就是判斷用戶是不是lhb并且其口令是不是awave如果是就返回一個表示匹配的字符串"match",否則,就拋出出錯信息。其代碼清單如下:
|
將其放在bussness包中。
我們同樣要將其表示錯誤信息的鍵值設置在ApplicationResource.properties文件中,清單如下:
|
到此為止,我們已經完成了這個簡單登錄程序的所有組件。下面就可以享受我們的勞動成果了。
第六步、編譯運行應用程序。
常規的做法是用Ant來裝配和部署Struts應用程序,如果按這個套路,這篇文章就會顯得十分冗長乏味,同時也沒有太大的必要,因為,用一個IDE一般可以很方便地生成一個應用。因此,我們采用簡便的方法,直接編譯我們的.java文件。不過這里要注意一點的是:實踐證明,要使得編譯過程不出錯,還必須將struts.jar文件放一份拷貝到
|
啟動,tomcat。在瀏覽器中輸入:http://localhost:8080/mystruts/logon.jsp
如果前面的步驟沒有紕漏的話,一個如圖所示的登錄畫面就會出現在你的眼前。

如果,不輸入任何內容直接點擊Submit按鈕,就會返回到logon.jsp并顯示missing username和missing password錯誤信息;如果輸入其他內容,則會返回no matched user的錯誤;如果輸入的用戶名是lhb且口令是awave則會顯示表示登錄成功的歡迎頁面。
上面雖然是一個功能很簡單的應用程序,但麻雀雖小,五臟俱全,基本涉及到了struts的主要組成部分。下面我們就來分析一下程序的特點和基本的工作原理。
首先,我們在瀏覽器中輸入.jsp文件時,后臺將struts的自定義標簽"翻譯"成普通的html標簽返回給瀏覽器,而一些提示信息如作為輸入框label的username、password還有按鈕上提示信息還有錯誤信息等都來自MessageResources即ApplicationResource.properties文件中對應的鍵值。當我們點擊Submit按鈕時,從web.xml的配置可以看出,請求將被ActionServlet截獲。它通過表單中提供的action參數在struts-config.xml文件中查找對應的
大家一定注意到了,在本例的業務邏輯模塊UserInfoBo中,將用戶與密碼是寫死在程序中的,在一個真實的應用程序中是不會這樣做的,那些需要永久保存的信息如,username及口令等都會保存在數據庫文件之類的永久介質中,下一篇文章我們將介紹在struts中如何訪問數據庫。