運(yùn)用Jakarta Struts的七大實(shí)戰(zhàn)心法
(原文 http://www.onjava.com/pub/a/onjava/2002/10/30/jakarta.html?page=1)
(作者Chuck Cavaness, 編譯 邱文宇)
編者按:當(dāng)作者 Chuck Cavaness(著有《Programming Jakarta Struts》一書)所在的網(wǎng)絡(luò)公司決定采用Struts框架之后,Chuck曾經(jīng)花費(fèi)了好幾個(gè)月來研究如何用它來構(gòu)建公司的應(yīng)用系統(tǒng)。本文敘述的正是作者在運(yùn)用Struts過程中來之不易的若干經(jīng)驗(yàn)和心得。如果你是個(gè)負(fù)責(zé)通過jsp和servlet開發(fā)Web應(yīng)用的Java程序員,并且也正在考慮采用基于Struts的構(gòu)建方法的話,那么你會(huì)在這里發(fā)現(xiàn)很多頗有見地同時(shí)也很有價(jià)值的信息。
1. 只在必要的時(shí)候才考慮擴(kuò)展Struts框架
一個(gè)好的framework有很多優(yōu)點(diǎn),首先,它必須能夠滿足用戶的可預(yù)見的需求。為此 Struts為Web 應(yīng)用提供了一個(gè)通用的架構(gòu),這樣開發(fā)人員可以把精力集中在如何解決實(shí)際業(yè)務(wù)問題上。其次,一個(gè)好的framework還必須能夠在適當(dāng)?shù)牡胤教峁U(kuò)展接口,以便應(yīng)用程序能擴(kuò)展該框架來更好的適應(yīng)使用者的實(shí)際需要。
如果Struts framework在任何場(chǎng)合,任何項(xiàng)目中都能很好的滿足需求,那真是太棒了。但是實(shí)際上,沒有一個(gè)框架聲稱能做到這一點(diǎn)。一定會(huì)有一些特定的應(yīng)用需求是框架的開發(fā)者們無法預(yù)見到的。因此,最好的辦法就是提供足夠的擴(kuò)展接口,使得開發(fā)工程師能夠調(diào)整struts來更好的符合他們的特殊要求。
在Struts framework中有很多地方可供擴(kuò)展和定制。幾乎所有的配置類都能被替換為某個(gè)用戶定制的版本,這只要簡(jiǎn)單的修改一下Struts的配置文件就可以做到。
其他組件如ActionServlet和 RequestProcessor 也能用自定義的版本代替. 甚至連Struts 1.1里才有的新特性也是按照擴(kuò)展的原則來設(shè)計(jì)的。例如,在異常處理機(jī)制中就允許用戶定制異常處理的句柄,以便更好的對(duì)應(yīng)用系統(tǒng)發(fā)生的錯(cuò)誤做出響應(yīng)。
作為框架的這種可調(diào)整特性在它更適合你的應(yīng)用的同時(shí)也在很大的程度上影響了項(xiàng)目開發(fā)的效果。首先,由于您的應(yīng)用是基于一個(gè)現(xiàn)有的成熟的、穩(wěn)定的framework如Struts,測(cè)試過程中發(fā)現(xiàn)的錯(cuò)誤數(shù)量將會(huì)大大減少,同時(shí)也能縮短開發(fā)時(shí)間和減少資源的投入。因?yàn)槟悴辉傩枰度腴_發(fā)力量用于編寫基礎(chǔ)框架的代碼了。
然而, 實(shí)現(xiàn)更多的功能是要花費(fèi)更大的代價(jià)的。我們必須小心避免不必要的濫用擴(kuò)展性能, Struts是由核心包加上很多工具包構(gòu)成的,它們已經(jīng)提供了很多已經(jīng)實(shí)現(xiàn)的功能。因此不要盲目的擴(kuò)展Struts框架,要先確定能不能采用其他方法使用現(xiàn)有的功能來實(shí)現(xiàn)。 在決定編寫擴(kuò)展代碼前務(wù)必要確認(rèn)Struts的確沒有實(shí)現(xiàn)你要的功能。否則重復(fù)的功能會(huì)導(dǎo)致混亂將來還得花費(fèi)額外的精力清除它。
2. 使用異常處理聲明
要定義應(yīng)用程序的邏輯流程,成熟的經(jīng)驗(yàn)是推薦在代碼之外,用配置的方法來實(shí)現(xiàn),而不是寫死在程序代碼中的。在J2EE中,這樣的例子比比皆是。從實(shí)現(xiàn)EJB的安全性和事務(wù)性行為到描述JMS消息和目的地之間的關(guān)系,很多運(yùn)行時(shí)的處理流程都是可以在程序之外定義的。
Struts 創(chuàng)建者從一開始就采用這種方法,通過配置Struts的配置文件來定制應(yīng)用系統(tǒng)運(yùn)行時(shí)的各個(gè)方面。這一點(diǎn)在版本1.1的新特性上得到延續(xù),包括新的異常處理功能。在Struts framework以前的版本中,開發(fā)人員不得不自己處理Struts應(yīng)用中發(fā)生的錯(cuò)誤情況。在最新的版本中,情況大大的改觀了,Struts Framework提供了內(nèi)置的一個(gè)稱為 ExceptionHandler 的類, 用于系統(tǒng)缺省處理action類運(yùn)行中產(chǎn)生的錯(cuò)誤。這也是在上一個(gè)技巧中我們提到的framework許多可擴(kuò)展接口之一。
Struts缺省的 ExceptionHandler類會(huì)生成一個(gè)ActionError對(duì)象并保存在適當(dāng)?shù)姆秶╯cope)對(duì)象中。這樣就允許JSP頁面使用錯(cuò)誤類來提醒用戶出現(xiàn)什么問題。如果你認(rèn)為這不能滿足你的需求,那么可以很方便的實(shí)現(xiàn)你自己的ExcepionHandler類。
具體定制異常處理的方法和機(jī)制
要定制自己的異常處理機(jī)制,第一步是繼承org.apache.struts.action.ExceptionHandler類。這個(gè)類有2個(gè)方法可以覆蓋,一個(gè)是excute()另外一個(gè)是storeException(). 在多數(shù)情況下,只需要覆蓋其中的excute()方法。下面是ExceptionHandler類的excute()方法聲明:
正如你看到的,該方法有好幾個(gè)參數(shù),其中包括原始的異常。方法返回一個(gè)ActionForward對(duì)象,用于異常處理結(jié)束后將controller類帶到請(qǐng)求必須轉(zhuǎn)發(fā)的地方去。
當(dāng)然您可以實(shí)現(xiàn)任何處理,但一般而言,我們必須檢查拋出的異常,并針對(duì)該類型的異常進(jìn)行特定的處理。缺省的,系統(tǒng)的異常處理功能是創(chuàng)建一個(gè)出錯(cuò)信息,同時(shí)把請(qǐng)求轉(zhuǎn)發(fā)到配置文件中指定的地方去。 定制異常處理的一個(gè)常見的例子是處理嵌套異常。假設(shè)該異常包含有嵌套異常,這些嵌套異常又包含了其他異常,因此我們必須覆蓋原來的execute()方法,對(duì)每個(gè)異常編寫出錯(cuò)信息。
一旦你創(chuàng)建了自己的ExceptionHandler 類,就應(yīng)該在Struts配置文件中的部分聲明這個(gè)類,以便讓Struts知道改用你自定義的異常處理取代缺省的異常處理.
可以配置你自己的ExceptionHandler 類是用于Action Mapping特定的部分還是所有的Action對(duì)象。如果是用于Action Mapping特定的部分就在<action>元素中配置。如果想讓這個(gè)類可用于所有的Action對(duì)象,可以在<global-sections> 元素中指定。例如,假設(shè)我們創(chuàng)建了異常處理類CustomizedExceptionHandler用于所有的Action類, <global-exceptions>元素定義如下所示:
在<exception />元素中可以對(duì)很多屬性進(jìn)行設(shè)置。在本文中,最重要的屬性莫過于handler屬性, handler屬性的值就是自定義的繼承了ExceptionHandler類的子類的全名。 假如該屬性沒有定義,Struts會(huì)采用自己的缺省值。當(dāng)然,其他的屬性也很重要,但如果想覆蓋缺省的異常處理的話,handler無疑是最重要的屬性。
最后必須指出的一點(diǎn)是,你可以有不同的異常處理類來處理不同的異常。在上面的例子中,CustomizedExceptionHandler用來處理任何java.lang.Exception的子類. 其實(shí),你也可以定義多個(gè)異常處理類,每一個(gè)專門處理不同的異常樹。下面的XML片斷解釋了如何配置以實(shí)現(xiàn)這一點(diǎn)。
在這里,一旦有異常拋出,struts framework將試圖在配置文件中找到ExceptionHandler,如果沒有找到,那么struts將沿著該異常的父類鏈一層層往上找直到發(fā)現(xiàn)匹配的為止。因此,我們可以定義一個(gè)層次型的異常處理關(guān)系結(jié)構(gòu),在配置文件中已經(jīng)體現(xiàn)了這一點(diǎn)。
3. 使用應(yīng)用模塊(Application Modules)
Struts 1.1的一個(gè)新特性是應(yīng)用模塊的概念。應(yīng)用模塊允許將單個(gè)Struts應(yīng)用劃分成幾個(gè)模塊,每個(gè)模塊有自己的Struts配置文件,JSP頁面,Action等等。這個(gè)新特性是為了解決大中型的開發(fā)隊(duì)伍抱怨最多的一個(gè)問題,即為了更好的支持并行開發(fā)允許多個(gè)配置文件而不是單個(gè)配置文件。
注:在早期的beta版本中,該特性被稱為子應(yīng)用(sub-applications),最近的改名目的是為了更多地反映它們?cè)谶壿嬌系姆止ぁ?br>
顯然,當(dāng)很多開發(fā)人員一起參加一個(gè)項(xiàng)目時(shí),單個(gè)的Struts配置文件很容易引起資源沖突。應(yīng)用模塊允許Struts按照功能要求進(jìn)行劃分,許多情況已經(jīng)證明這樣更貼近實(shí)際。例如,假設(shè)我們要開發(fā)一個(gè)典型的商店應(yīng)用程序??梢詫⒔M成部分劃分成模塊比如catalog(商品目錄), customer(顧客), customer service(顧客服務(wù)), order(訂單)等。每個(gè)模塊可以分布到不同的目錄下,這樣各部分的資源很容易定位,有助于開發(fā)和部署。圖1 顯示了該應(yīng)用的目錄結(jié)構(gòu)。
圖 1. 一個(gè)典型的商店應(yīng)用程序的目錄結(jié)構(gòu)
注:如果你無需將項(xiàng)目劃分成多個(gè)模塊,Struts框架支持一個(gè)缺省的應(yīng)用模塊。這就使得應(yīng)用程序也可以在1.0版本下創(chuàng)建,具有可移植性,因?yàn)閼?yīng)用程序會(huì)自動(dòng)作為缺省的應(yīng)用模塊。
為了使用多應(yīng)用模塊功能,必須執(zhí)行以下幾個(gè)準(zhǔn)備步驟:
• 為每個(gè)應(yīng)用模塊創(chuàng)建獨(dú)立的Struts配置文件。
• 配置Web 部署描述符 Web.xml文件。
• 使用org.apache.struts.actions.SwitchAction 來實(shí)現(xiàn)程序在模塊之間的跳轉(zhuǎn).
創(chuàng)建獨(dú)立的Struts配置文件
每個(gè)Struts應(yīng)用模塊必須擁有自己的配置文件。允許創(chuàng)建自己的獨(dú)立于其他模塊的Action,ActionForm,異常處理甚至更多。
繼續(xù)以上面的商店應(yīng)用程序?yàn)槔覀兛梢詣?chuàng)建以下的配置文件:一個(gè)文件名為struts-config-catalog.xml,包含catalog(商品目錄)、items(商品清單)、和其它與庫(kù)存相關(guān)的功能的配置信息;另一個(gè)文件名為struts- config-order.xml, 包含對(duì)order(訂單)和order tracking(訂單跟蹤)的設(shè)置。第三個(gè)配置文件是struts-config.xml,其中含有屬于缺省的應(yīng)用模塊中的一般性的功能。
配置Web部署描述符
在Struts的早期版本中,我們?cè)赪eb.xml中指定Struts配置文件的路徑。好在這點(diǎn)沒變,有助于向后兼容。但對(duì)于多個(gè)應(yīng)用模塊,我們需要在Web部署描述符中增加新的配置文件的設(shè)定。
對(duì)于缺省的應(yīng)用(包括Struts的早期版本),Struts framework 在Web.xml文件中查找?guī)в衏onfig的元素<init-param>,用于載入Action mapping 和其它的應(yīng)用程序設(shè)定。作為例子,以下的XML片斷展現(xiàn)一個(gè)典型的<init-param>元素:
注:如果在現(xiàn)有的<init-param>元素中找不到"config"關(guān)鍵字,Struts framework將缺省地使用/WEB/struts-config.xml
為了支持多個(gè)應(yīng)用模塊(Struts 1.1的新特性),必須增加附加的<init-param>元素。與缺省的<init-param>元素不同的是,附加的<init-param>元素與每個(gè)應(yīng)用模塊對(duì)應(yīng),必須以config/xxx的形式命名,其中字符串xxx代表該模塊唯一的名字。例如,在商店應(yīng)用程序的例子中,<init-param>元素可定義如下(注意粗體字部分):
第一個(gè) <init-param>元素對(duì)應(yīng)缺省的應(yīng)用模塊。第二和第三個(gè)元素分別代表非缺省應(yīng)用模塊catalog 和 order。
當(dāng)Struts載入應(yīng)用程序時(shí),它首先載入缺省應(yīng)用模塊的配置文件。然后查找?guī)в凶址甤onfig/xxx 形式的附加的初始化參數(shù)。對(duì)每個(gè)附加的配置文件也進(jìn)行解析并載入內(nèi)存。這一步完成后,用戶就可以很隨意地用config/后面的字符串也就是名字來調(diào)用相應(yīng)的應(yīng)用模塊。
多個(gè)應(yīng)用模塊之間調(diào)用Action類
在為每個(gè)應(yīng)用模塊創(chuàng)建獨(dú)立的配置文件之后,我們就有可能需要調(diào)用不同的模塊中Action。為此必須使用Struts框架提供的SwitchAction類。Struts 會(huì)自動(dòng)將應(yīng)用模塊的名字添加到URL,就如Struts 自動(dòng)添加應(yīng)用程序的名字加到URL一樣。應(yīng)用模塊是對(duì)框架的一個(gè)新的擴(kuò)充,有助于進(jìn)行并行的團(tuán)隊(duì)開發(fā)。如果你的團(tuán)隊(duì)很小那就沒必要用到這個(gè)特性,不必進(jìn)行模塊化。當(dāng)然,就算是只有一個(gè)模塊,系統(tǒng)還是一樣的運(yùn)作。
4. 把JSP放到WEB-INF后以保護(hù)JSP源代碼
為了更好地保護(hù)你的JSP避免未經(jīng)授權(quán)的訪問和窺視, 一個(gè)好辦法是將頁面文件存放在Web應(yīng)用的WEB-INF目錄下。
通常JSP開發(fā)人員會(huì)把他們的頁面文件存放在Web應(yīng)用相應(yīng)的子目錄下。一個(gè)典型的商店應(yīng)用程序的目錄結(jié)構(gòu)如圖2所示。跟catalog (商品目錄)相關(guān)的JSP被保存在catalog子目錄下。跟customer相關(guān)的JSP,跟訂單相關(guān)的JSP等都按照這種方法存放。
圖 2.基于不同的功能 JSP 被放置在不同的目錄下
這種方法的問題是這些頁面文件容易被偷看到源代碼,或被直接調(diào)用。某些場(chǎng)合下這可能不是個(gè)大問題,可是在特定情形中卻可能構(gòu)成安全隱患。用戶可以繞過Struts的controller直接調(diào)用JSP同樣也是個(gè)問題。
為了減少風(fēng)險(xiǎn),可以把這些頁面文件移到WEB-INF 目錄下?;赟ervlet的聲明,WEB-INF不作為Web應(yīng)用的公共文檔樹的一部分。因此,WEB-INF 目錄下的資源不是為客戶直接服務(wù)的。我們?nèi)匀豢梢允褂肳EB-INF目錄下的JSP頁面來提供視圖給客戶,客戶卻不能直接請(qǐng)求訪問JSP。
采用前面的例子,圖3顯示將JSP頁面移到WEB-INF 目錄下后的目錄結(jié)構(gòu)
圖 3. JSP存放在 WEB-INF 目錄下更為安全
如果把這些JSP頁面文件移到WEB-INF 目錄下,在調(diào)用頁面的時(shí)候就必須把"WEB-INF"添加到URL中。例如,在一個(gè)Struts配置文件中為一個(gè)logoff action寫一個(gè)Action mapping。其中JSP的路徑必須以"WEB-INF"開頭。如下所示:請(qǐng)注意粗體部分.
這個(gè)方法在任何情況下都不失為Struts實(shí)踐中的一個(gè)好方法。是唯一要注意的技巧是你必須把JSP和一個(gè)Struts action聯(lián)系起來。即使該Action只是一個(gè)很基本的很簡(jiǎn)單JSP,也總是要調(diào)用一個(gè)Action,再由它調(diào)用JSP。
最后要說明的是,并不是所有的容器都能支持這個(gè)特性。WebLogic早期的版本不能解釋Servlet聲明,因此無法提供支持,據(jù)報(bào)道在新版本中已經(jīng)改進(jìn)了??傊褂弥跋葯z查一下你的Servlet容器。
5. 使用 Prebuilt Action類提升開發(fā)效率
Struts framework帶有好幾個(gè)prebuilt Action類,使用它們可以大大節(jié)省開發(fā)時(shí)間。其中最有用的是org.apache.struts.actions.ForwardAction 和 org.apache.struts.actions.DispatchAction.
使用 ForwardAction
在應(yīng)用程序中,可能會(huì)經(jīng)常出現(xiàn)只要將Action對(duì)象轉(zhuǎn)發(fā)到某個(gè)JSP的情況。在上一點(diǎn)中曾提到總是由Action調(diào)用JSP是個(gè)好習(xí)慣。如果我們不必在Action中執(zhí)行任何業(yè)務(wù)邏輯,卻又想遵循從Action訪問頁面的話,就可以使用ForwardAction,它可以使你免去創(chuàng)建許多空的Action類。運(yùn)用ForwardAction的好處是不必創(chuàng)建自己的Action類,你需要做的僅僅是在Struts配置文件中配置一個(gè)Action mapping。
舉個(gè)例子,假定你有一個(gè)JSP文件index.jsp ,而且不能直接調(diào)用該頁面,必須讓程序通過一個(gè)Action類調(diào)用,那么,你可以建立以下的Action mapping來實(shí)現(xiàn)這一點(diǎn):
正如你看到的,當(dāng) /home 被調(diào)用時(shí), 就會(huì)調(diào)用ForwardAction 并把請(qǐng)求轉(zhuǎn)發(fā)到 index.jsp 頁面.
再討論一下不通過一個(gè)Action類直接轉(zhuǎn)發(fā)到某個(gè)頁面的情況,必須注意我們?nèi)匀皇褂?lt;action>元素中的forward屬性來實(shí)現(xiàn)轉(zhuǎn)發(fā)的目標(biāo)。這時(shí)<action>元素定義如下:
以上兩種方法都可以節(jié)省你的時(shí)間,并有助于減少一個(gè)應(yīng)用所需的文件數(shù)。
使用 DispatchAction
DispatchAction是Struts包含的另一個(gè)能大量節(jié)省開發(fā)時(shí)間的Action類。與其它Action類僅提供單個(gè)execute()方法實(shí)現(xiàn)單個(gè)業(yè)務(wù)不同,DispatchAction允許你在單個(gè)Action類中編寫多個(gè)與業(yè)務(wù)相關(guān)的方法。這樣可以減少Action類的數(shù)量,并且把相關(guān)的業(yè)務(wù)方法集合在一起使得維護(hù)起來更容易。
要使用DispatchAction的功能,需要自己創(chuàng)建一個(gè)類,通過繼承抽象的DispatchAction得到。對(duì)每個(gè)要提供的業(yè)務(wù)方法必須有特定的方法signature。例如,我們想要提供一個(gè)方法來實(shí)現(xiàn)對(duì)購(gòu)物車添加商品清單,創(chuàng)建了一個(gè)類ShoppingCartDispatchAction提供以下的方法:
那么,這個(gè)類很可能還需要一個(gè)deleteItem()方法從客戶的購(gòu)物車中刪除商品清單,還有clearCart()方法清除購(gòu)物車等等。這時(shí)我們就可以把這些方法集合在單個(gè)Action類,不用為每個(gè)方法都提供一個(gè)Action類。
在調(diào)用ShoppingCartDispatchAction里的某個(gè)方法時(shí),只需在URL中提供方法名作為參數(shù)值。就是說,調(diào)用addItem()方法的 URL看起來可能類似于:
http://myhost/storefront/action/cart?method=addItem
其中method參數(shù)指定ShoppingCartDispatchAction中要調(diào)用的方法。參數(shù)的名稱可以任意配置,這里使用的"method"只是一個(gè)例子。參數(shù)的名稱可以在Struts配置文件中自行設(shè)定。
6.使用動(dòng)態(tài)ActionForm
在Struts framework中,ActionForm對(duì)象用來包裝HTML表格數(shù)據(jù)(包括請(qǐng)求),并返回返回動(dòng)態(tài)顯示給用戶的數(shù)據(jù)。它們必須是完全的JavaBean,并繼承.Struts 里面的ActionForm類,同時(shí),用戶可以有選擇地覆蓋兩個(gè)缺省方法。
該特性能節(jié)省很多時(shí)間,因?yàn)樗梢詤f(xié)助進(jìn)行自動(dòng)的表現(xiàn)層的驗(yàn)證。ActionForm的唯一缺點(diǎn)是必須為不同的HTML表格生成多個(gè)ActionForm 類以保存數(shù)據(jù)。例如,如果有一個(gè)頁面含有用戶的注冊(cè)信息,另一個(gè)頁面則含有用戶的介紹人的信息,那么就需要有兩個(gè)不同的ActionForm類。這在大的應(yīng)用系統(tǒng)中就會(huì)導(dǎo)致過多的ActionForm類。Struts 1.1對(duì)此做出了很好的改進(jìn),引入了動(dòng)態(tài)ActionForm類概念
通過Struts framework中的DynaActionForm類及其子類可以實(shí)現(xiàn)動(dòng)態(tài)的ActionForm ,動(dòng)態(tài)的ActionForm允許你通過Struts的配置文件完成ActionForm的全部配置;再也沒有必要在應(yīng)用程序中創(chuàng)建具體的ActionForm類。具體配置方法是:在Struts的配置文件通過增加一個(gè)<form-bean>元素,將type屬性設(shè)定成DynaActionForm或它的某個(gè)子類的全名。下面的例子創(chuàng)建了一個(gè)動(dòng)態(tài)的ActionForm名為logonForm,它包含兩個(gè)實(shí)例變量:username 和 password.
動(dòng)態(tài)的ActionForm可以用于Action類和JSP,使用方法跟普通的ActionForm相同,只有一個(gè)小差別。如果使用普通的ActionForm對(duì)象則需要提供get 和 set方法取得和設(shè)置數(shù)據(jù)。以上面的例子而言,我們需要提供getUsername() 和 setUsername()方法取得和設(shè)置username變量,同樣地有一對(duì)方法用于取得和設(shè)置password變量.
這里我們使用的是DynaActionForm,它將變量保存在一個(gè)Map類對(duì)象中,所以必須使用DynaActionForm 類中的get(name) 和 set(name)方法,其中參數(shù)name是要訪問的實(shí)例變量名。例如要訪問DynaActionForm中username的值,可以采用類似的代碼:
String username = (String)form.get("username");
由于值存放在一個(gè)Map對(duì)象,所以要記得對(duì)get()方法返回的Object對(duì)象做強(qiáng)制性類型轉(zhuǎn)換。
DynaActionForm有好幾個(gè)很有用的子類。其中最重要的是DynaValidatorForm ,這個(gè)動(dòng)態(tài)的ActionForm和Validator 一起利用公共的Validator包來提供自動(dòng)驗(yàn)證。這個(gè)特性使你得以在程序代碼之外指定驗(yàn)證規(guī)則。將兩個(gè)特性結(jié)合使用對(duì)開發(fā)人員來說將非常有吸引力。
7. 使用可視化工具
自從Struts 1.0 分布以來,就出現(xiàn)了不少可視化工具用于協(xié)助創(chuàng)建,修改和維護(hù)Struts的配置文件。配置文件本身是基于XML格式,在大中型的開發(fā)應(yīng)用中會(huì)增大變得很笨拙。為了更方便的管理這些文件,一旦文件大到你無法一目了然的時(shí)候,建議試著采用其中的一種GUI 工具協(xié)助開發(fā)。商業(yè)性的和開放源代碼的工具都有不少,表1列出了可用的工具和其相關(guān)鏈接,從那里可以獲取更多信息。
表 1. Struts GUI 工具
相關(guān)資源
要獲取更為全面的Struts GUI 工具列表 (包括免費(fèi)的和商業(yè)性的), 請(qǐng)?jiān)L問 Struts resource page.