? Struts快速學習指南 素材來自于《Programming Jakarta Struts》一書??
1.???? Struts簡介
Struts 是一個技術框架,由Craig R. McClanahan編寫,并且在2000年的時候捐獻給了ASF,目前,有很多組織和個人參與Struts框架的開發,使得Struts保持高速成長,同時,利用Struts開發的應用越來越多,使其成為web應用MVC模式中VC部分事實上的標準。
1.1? Web技術歷史
1.1.1?????????? CGI
web應用開發中歷史上,CGI(common gateway interface)是最早使用的一種技術,通過為不同的平臺,不同的web server編寫插件編寫應用接口,來滿足通過web方式編寫應用的需求。當時流行的方式包含NSAPI/ISAPI,使用Perl來編寫CGI程序。CGI最大的問題就是線程并發的問題,當時給很多人的感覺是CGI訪問速度慢,其主要原因是應用程序所編寫的CGI沒有考慮多線程。
1.1.2?????????? Servlet
??? 作為一種跨平臺語言的服務器端技術,其一經產生就備受矚目,采用Servlet開發的應用,不用考慮平臺,多線程等讓人頭疼的問題,使得開發人員專注于業務邏輯的實現,大大解放了生產力。但是,在Servlet中嵌入html無疑是開發人員的噩夢,與同時期微軟的ASP相比,Servlet在開發效率方面讓人不敢恭維。
1.1.3?????????? Java Server Pages
JSP從很大程度上時參考了ASP的想法,使得采用Java語言開發服務器端應用非常容易,同時因為java與生俱來的跨平臺、安全性、易用性優勢,當然,還有開發人員的高工資 ,使得JSP逐漸在Web服務器端應用開發中占據了主流位置。
?
2.???? Struts安裝
Struts作為一個J2EE 框架,很容易和你的web應用結合起來,你僅僅需要作以下幾個步驟:
1、? 下在Struts1.1 二進制壓縮包,將壓縮包解壓到%STRUTS_HOME%目錄,目錄結構如下如示:
?
2、? 建立你的標準web應用程序,所謂標準應用程序是指在web應用程序的根目錄下有一個WEB-INFO目錄,WEB-INF下有classes,lib目錄,classes下面有個web.xml文件。本文后續假設你的web應用在%WEB_ROOT%目錄下。
3、? 將%STRUTS_HOME%/lib下所有文件copy到%WEB_ROOT%/WEB-INF/lib下。
4、? 配置%WEB_ROOT%/WEB-INF/classes/web.xml以滿足Struts需要,具體如下:
1、? 在配置文件中映射ActionServlet,ActionServlet用于接受所有訪問者的請求。在Struts應用中,所有對應用程序的請求,都會被WEB SERVER定向到ActionServlet進行統一控制、分配處理,ActionServlet可以看作是Struts框架的核心,樞紐。
?? <web-app>?
?<servlet>
? <servlet-name>controller</servlet-name>
? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
?</servlet>
</web-app>
?
2、? 配置servlet映射,通過servlet映射可以將用戶訪問web應用的擴展名映射到具體處理的servlet,例如,將所有以.do為擴展名的頁面的請求交給ActionServlet處理。
<web-app>?
?<servlet>
? <servlet-name>controller</servlet-name>
? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
?</servlet>
?<servlet-mapping>
? <servlet-name>controller</servlet-name>
? <url-pattern>*.do</url-pattern>
?</servlet-mapping>
</web-app>
? 另外,也可以采用如下方式進行映射,該方式將所有對/action/目錄下文件的訪問請求交給ActionServlet處理。
<web-app>?
?<servlet>
? <servlet-name> controller </servlet-name>
? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
?</servlet>
?<servlet-mapping>
? <servlet-name>controller</servlet-name>
? <url-pattern>>/action/*</url-pattern>
?</servlet-mapping>
</web-app>
?
3、? 配置ActionServlet的初始化參數,Struts1.1有一些指定的初始化參數,用于指明Struts應用所需要的配置文件,debug等級等。
<web-app>?
?<servlet>
? <servlet-name>controller</servlet-name>
? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
? <init-param>
?? <param-name>config</param-name>
?? <param-value>/WEB-INF/struts-config.xml</param-value>
? </init-param>
? <init-param>
?? <param-name>host</param-name>
?? <param-value>localhost</param-value>
? </init-param>
? <init-param>
?? <param-name>port</param-name>
?? <param-value>7001</param-value>
? </init-param>
?</servlet>
?<servlet-mapping>
? <servlet-name> controller </servlet-name>
? <url-pattern>*.do</url-pattern>
?</servlet-mapping>
</web-app>
初始化參數利用<init-param>進行配置,配置采用名稱-值對的方式,一個<param-name>對應一個<param-value>,初始化參數可以任意定義,例如host,port,但是有一些在Struts1.1中是具有特別意義的,列舉如下:
表2-1. Struts1.1中用到的初始化參數
參數名?含義/默認值
config ?以相對路徑的方式指明Struts應用程序的配置文件位置。如不設置,則默認值為/WEB-INF/struts-config.xml。
config/sub1 ?以相對路徑的方式指明子應用程序的配置文件位置,一般來說,很少用到子應用程序,在此不多描述。
debug ?設置Servlet的debug級別,控制日志記錄的詳細程度。默認為0,記錄相對最少的日志信息。
detail ?設置Digester的debug級別,Digester是Struts框架所使用的用來解析xml配置文件的一個框架,通過該設置,可以查看不同詳細等級的解析日志。默認為0,記錄相對最少的日志信息。
4、? 配置標簽庫,標簽庫是Struts自帶的一些組件庫,采用JSP規范中Tag-lib的方式供大家使用,正是因為存在這么豐富的標簽庫,使得采用Struts的開發才顯得這么方便,高效。
? <web-app>
?<servlet>
? <servlet-name>controller</servlet-name>
? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
? <init-param>
?? <param-name>config</param-name>
?? <param-value>/WEB-INF/struts-config.xml</param-value>
? </init-param>
? <init-param>
?? <param-name>host</param-name>
?? <param-value>localhost</param-value>
? </init-param>
? <init-param>
?? <param-name>port</param-name>
?? <param-value>7001</param-value>
? </init-param>
?</servlet>
?
?<servlet-mapping>
? <servlet-name>controller</servlet-name>
? <url-pattern>*.do</url-pattern>
?</servlet-mapping>
?
?<taglib>
? <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
?</taglib>
?
?<taglib>
? <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
?</taglib>
?
?<taglib>
? <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
?</taglib>
</web-app>
標簽庫采用<taglib>定義,<taglib>含有兩個子元素,<taglib-uri>和<taglib-location>,<taglib-uri>用戶定義標簽庫的唯一表示符,可以理解為名字,以后要在jsp頁面中使用這個標簽庫,靠的就是它。<taglib-location>指明標簽庫存在的物理路徑,當然,和配置文件一樣,也是相對路徑。
5、? 設置welcome文件列表(可選步驟)
<welcome-file-list>
?<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
6、? 設置錯誤處理(可選步驟),通常的http訪問異常包含404 Not Found和500 Internal Error,為了提供給用戶更為友好的顯示,可以做如下配置:
<web-app>
?<error-page>
? <error-code>404</error-code>
? <location>/common/404.jsp</location>
?</error-page>
?
?<error-page>
? <error-code>500</error-code>
? <location>/common/500.jsp</location>
?</error-page>
</web-app>
通過如上配置,當用戶訪問應用中不存在的頁面時,將會將用戶導向到/common/404.jsp頁面。同樣地,當出現異常錯誤時,將會把/common/500.jsp顯示給用戶。
7、? 最后,一個完整的web.xml示例如下:
<?xml version="1.0" encoding="UTF-8"?>
?
<!DOCTYPE web-app
??? PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
??? "<web-app>?
?<servlet>
? <servlet-name>storefront</servlet-name>
? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
? <init-param>
?? <param-name>config</param-name>
?? <param-value>/WEB-INF/struts-config.xml</param-value>
? </init-param>???
? <init-param>
?? <param-name>debug</param-name>
?? <param-value>3</param-value>
? </init-param>
? <init-param>
?? <param-name>detail</param-name>
?? <param-value>3</param-value>
? </init-param>
? <load-on-startup>1</load-on-startup>
?</servlet>
?
?<servlet-mapping>
? <servlet-name>storefront</servlet-name>
? <url-pattern>/action/*</url-pattern>
?</servlet-mapping>
?
?<welcome-file-list>
? <welcome-file>index.jsp</welcome-file>??
?</welcome-file-list>
?
?<error-page>
? <error-code>404</error-code>
? <location>/common/404.jsp</location>
?</error-page>
?<error-page>
? <error-code>500</error-code>
? <location>/common/500.jsp</location>
?</error-page>
?
?<taglib>
? <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
?</taglib>
?<taglib>
? <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
?</taglib>
?<taglib>
? <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
?</taglib>
</web-app>
1、? 到此為止,Struts的開發環境安裝算是告一段落。
1.???? Struts框架
在介紹Struts框架之前,先來看看web開發的兩種模式,這兩種模式自JSP開發流行以來,就爭論不斷,它們分別是JSP Model 1和JSP Model 2。
1.1? JSP Model 1
下圖是JSP Model 1 的構架示意圖:
?
用戶通過瀏覽器之間訪問web應用的JSP頁面, JSP提供UI顯示,JavaBeans處理數據庫訪問和業務邏輯。這種開發方式最大的優勢是直接、簡單,對于小型應用,可以很方便、快速地進行開發。
1.2? JSP Model 2
下圖是JSP Model 2 的構架示意圖:
?
JSP Model 2 和JSP Model 1 最大的區別是引入了MVC模式的概念,即M(Model:業務邏輯),V(View:系統UI),C(Controller:控制)分離,用戶的所有請求提交給Controller,由Controller進行統一分配,并且采用推的方式將不同的UI顯示給用戶。這樣做得好處是:
1、? 可以統一控制用戶的行為,例如在Controller中添加統一日志記錄等功能是非常方便的。
2、? 職責分離,有利于各部分的維護。用戶不直接訪問分散的UI,這樣可以通過配置文件或則流程定義的方式,在不同的環節、時間將不同的頁面推向給用戶。
?
1.3? Struts
通過了解JSP Model 1和JSP Model 2,我想大家心里都已經有了選擇,在這里,我不想說哪一種構架更好,在不同的環境中,使用恰到好處的技術才是最好的。普遍來說,MVC分離是個不錯的選擇。
Struts框架正是MVC分離的一個杰出作品。首先我們來看一下Struts1.1的UML圖,以便于我們對Struts有個全局的了解:
?
先不用急著看懂這張圖,在下面的學習過程中,我們會慢慢地了解這張圖中各個組件的含義。
接下來,我們從MVC的角度對Struts框架進行探索。
1.3.1?????????? Controller
首先介紹MVC中的C,上面提到了,JSP Model 1 和JSP Model 2 最大的卻別就是C,那么在Struts中,這個C是什么呢?他是如何實現的呢?下面我們再來看看這個圖:
?這是JSP Model 2的構架圖,也是Struts的構架圖,Struts使用一個Servlet作為Controller,處理用戶的請求,并分派給Model進行業務處理,在合適的時候將合適的View推向給用戶。這個Servlet是org.apache.struts.action.ActionServlet或其子類。ActionServlet類擴展自javax.servlet.http.HttpServlet類,其職責是將http請求提交給合適的處理器(Processor)進行處理。關于處理器我們在稍后會介紹,是org.apache.struts.action.RequestProcessor 或其子類的一個實例。
1.3.1.1??? Controller(控制器)機制
J2EE的前端控制器(Front Controller)設計模式中利用一個前端控制器來接受所有客戶請求,為應用提供一個中心控制點,在該控制點上,可以很方便地添加一些全局性的,如加密、國際化、日志等通用操作。Controller的實現機制正是建立在前端控制器的設計模式基礎上。
前面我們介紹過,Struts的控制器擁有一些職責,其中最主要的是以下幾個:
??????????????? 接收客戶請求。
??????????????? 映射請求到指定的業務操作。
??????????????? 獲取業務操作的結果并以有效的方式提供給客戶。
??????????????? 根據業務操作的結果和當前的狀態把不同的? 推向給客戶。
在Struts框架中,控制器中不同的組件負責不同的控制職責,下圖是Struts框架中關于控制器部分的一個組件圖:
?在上圖中,很明顯地可以看出,ActionServlet處于核心位置,那么,我們就先來了解一下ActionServlet。
1.3.1.2??? ActionServlet類
org.apache.struts.action.ActionServlet在????? 應用程序中扮演接收器的角色,所有客戶端的請求在被其它類處理之前都得通過ActionServlet 的控制。
當 ActionServlet的實例接收到一個??? 請求,不管是通過?? 方法或??? 方法,ActionServlet的process( )方法被調用并用以處理客戶請求。process( )方法實現顯示如下:
protected void process(HttpServletRequest request,HttpServletResponse response)
? throws IOException, ServletException {
?
? RequestUtils.selectApplication( request, getServletContext(? ) );
? getApplicationConfig( request ).getProcessor(? ).process( request, response );
}
該方法的實現很簡單,RequestUtils.selectApplication( request, getServletContext(? ) );語句是用來根據用戶訪問的上下文路徑來選擇處理的應用,如果你只有一個Struts配置文件,就表示你只有一個Struts應用。關于如何建立多個Struts應用,本教程不作詳細講解,請參考相應資料。getApplicationConfig( request ).getProcessor(? ).process( request, response );語句用來獲取一個處理器,并將客戶請求提交給處理器處理。
1.3.1.3??? Struts初始化處理流程
根據在?????? 中配置的初始化參數,?????? 容器將決定在在容器的第一次啟動,或第一次客戶請求ActionServlet的時機加載ActionServlet ,不管哪種方式加載,和其它?????? 一樣,ActionServlet的init( )方法將被調用,開始初始化過程。讓我們來看看在初始化過程中將發生些什么,理解了這些,對于我們???? 和擴展自己的應用更加得心應手。
?????????? 初始化框架的內部消息綁定,這些消息用來輸出提示,警告,和錯誤信息到日志文件中。org.apache.struts.action.ActionResources用來獲取內部消息;
?????????? 加載?????? 中定義的不同參數,用以控制ActionServlet的不同行為,這些參數包括config? debug? detail????? convertNull ;
?????????? 加載并初始化?????? 中定義的??????? 名稱和?????? 映射信息。通過初始化,框架的各種?? 被注冊,?? 用來在下一步校驗配置文件的有效性;
4、??????? 為默認應用加載并初始化????? 配置文件,配置文件即初始化參數config指定的文件。默認配置文件被解析,產生一個ApplicationConfig對象存于ServletContext中。可以通過關鍵字org.apache.struts.action.APPLICATION從ServletContext中獲取ApplicationConfig;
???????????????? 配置文件中指定的每一個消息資源都被加載,初始化,并存在ServletContext的合適區域 基于每個message-resources元素的key屬性 ,如果key屬性沒有設置,則為org.apache.struts.action.MESSAGE;
???????????????? 配置文件中聲明的每一個數據源被加載并且初始化,如果沒有配置數據源,這一步跳過;
?????????? 加載并初始化????? 配置文件中指定的插件。每一個插件的init()方法被調用;
?????????? 當默認應用加載完成,init()方法判斷是否有應用模塊需要加載,如果有,重復步驟 - 萬成應用模塊的加載。
下圖是對上面文字說明的圖形化表示:
?
RequestProcessor類
前面提到過,當ActionServlet接收到客戶請求后,會進行一連串的初始化操作,然后,就會將客戶請求轉交給合適的處理器進行處理,這個合適的處理器就是org.apache.struts.action.RequestProcessor或其子類的一個實例(根據Struts配置文件中的配置)。提供了默認實現,如果需要自定義這些行為,可以重載這個類定義自己的處理行為,當你想要自定義操作時,Struts推薦你重載這個類而不是ActionServlet。
下面的代碼片斷提供了RequestProcessor的默認行為實現代碼:
public void process(HttpServletRequest request, HttpServletResponse response)
? throws IOException, ServletException {
?
??? // Wrap multipart requests with a special wrapper
??? request = processMultipart(request);
?
??? // Identify the path component we will use to select a mapping
??? String path = processPath(request, response);
??? if (path == null) {
????? return;
??? }
??? if (log.isInfoEnabled(? )) {
????? log.info("Processing a '" + request.getMethod(? ) +
??????? "' for path '" + path + "'");
??? }
?
??? // Select a Locale for the current user if requested
??? processLocale(request, response);
?
??? // Set the content type and no-caching headers if requested
??? processContent(request, response);
??? processNoCache(request, response);
?
??? // General-purpose preprocessing hook
??? if (!processPreprocess(request, response)) {
????? return;
??? }
?
??? // Identify the mapping for this request
??? ActionMapping mapping = processMapping(request, response, path);
??? if (mapping == null) {
????? return;
??? }
?
??? // Check for any role required to perform this action
??? if (!processRoles(request, response, mapping)) {
????? return;
??? }
?
??? // Process any ActionForm bean related to this request
??? ActionForm form = processActionForm(request, response, mapping);
??? processPopulate(request, response, form, mapping);
??? if (!processValidate(request, response, form, mapping)) {
????? return;
??? }
?
??? // Process a forward or include specified by this mapping
??? if (!processForward(request, response, mapping)) {
????? return;
??? }
??? if (!processInclude(request, response, mapping)) {
????? return;
??? }
?
??? // Create or acquire the Action instance to process this request
??? Action action = processActionCreate(request, response, mapping);
??? if (action == null) {
????? return;
??? }
?
??? // Call the Action instance itself
??? ActionForward forward =
????? processActionPerform(request, response, action, form, mapping);
?
??? // Process the returned ActionForward instance
??? processActionForward(request, response, forward);
}
?
接下來,讓我們一步一步地了解process()方法到底做了什么。
1、??????????? 調用processMultipart( )方法。如果HttpServletRequest是POST方式,且請求為multipart/form-data ,Struts框架將請求對象包裝成處理multipart 請求專用的請求對象,否則,只是簡單地返回原有的請求對象。一般來說,除非需要處理文件上傳,否則不用關心multipart 功能的具體細節。
2、??????????? 調用processPath( ) 方法,該方法用來從請求URL中獲應用取路徑部分。獲取到的信息在稍后的步驟中用于選擇合適的Struts Action調用。
3、??????????? 調用processLocale( ) 方法處理一些國際化的事務。
4、?????????? 調用方法來決定processContent( )請求的content type編碼(encoding)方式。content type可以配合在配置文件中,也可以在jsp文件中配置,默認為text/html。
5、?????????? 根據noCache屬性的設置調用processNoCache( ) 方法,如果noCache設置為true。則添加合適的響應頭到響應對象中,使得頁面保留在瀏覽器的Cache中。這些響應頭包含Pragma, Cache-Control, 和Expires 。
6、??????????? 調用processPreprocess( )方法,這個方法在這兒設置一個鉤子,方法的默認實現只是簡單地返回true,這樣給了自定義處理器的開發者提供了一個合適的地方讓你添加自己的業務邏輯。因為這個方法在調用Action之前被調用,如果你重載這個方法,只需要返回false,則Action就不會被調用。例如,你可以重載這個方法用戶檢查客戶session,如果不通過就返回false。
7、??????????? 調用processMapping( )方法,根據客戶請求信息中的path信息來決定是否返回ActionMapping對象實例。如果不能夠找到path的映射,則客戶將會得到一個error響應。
8、??????????? 通過調用processRoles( )方法檢查是否為Action配置了安全角色。如果配置了角色要求,則請求對象的isUserInRole( )方法被調用,如果用戶屬于這些角色,則客戶會得到顯示一個error響應。
9、??????????? 調用processActionForm( )方法檢查是否存在為ActionMapping配置的ActionForm 。如果存在,則在有效區域內查找是否存在該ActionForm的實例,存在,則復用,不存在,則創建一個實例。然后將實例保存與再配置文件中配置好的有效區域(request,session,application)內,并用Action元素的name屬性作為該實例的關鍵字。
10、?????? 調用processPopulate( )方法,如果存來存在為ActionMapping配置的ActionForm,則封裝請求對象中的數據到ActionForm中,在進行封裝之前,先調用ActionForm的reset( )方法進行屬性值的默認化。
11、???????? 調用processValidate( )方法。如果ActionForm被配置好,并且action元素的屬性validate被設置為true ,則進一步調用validate( )方法進行規則校驗。如果validate( )方法校驗失敗,就會保存一個ActionErrors對象到請求區域中,請求將會自動重定向到action映射的input屬性所指定的頁面中。如果校驗通過或在action映射中沒有配置ActionForm,則繼續處理請求。
12、???????? 根據action映射是否配置了forward屬性或include屬性來決定下一步操作。如果配置了任意一個,則相應地調用RequestDispatcher對象的forward( )方法或include( )方法,調用后,對客戶請求的處理結束。否則,繼續處理請求。
13、???????? 調用processActionCreate( )方法創建或獲取一個Action對象實例處理請求。processActionCreate( )方法會在緩存中查找是否存在已經創建好的Action實例,如果存在,則復用,否則,則重新創建并將其村于緩存中。
14、?????? 調用processActionPerform( )方法,該方法用于在一個try/catch代碼塊中調用action實例的execute( )方法,這樣確保action的execute( )方法一旦發生執行異常能夠被RequestProcessor捕獲。
15、?????? 調用processActionForward( )方法,并傳入action的execute( )方法所返回的ActionForward對象實例,方法通過檢查ActionForward對象實例,決定采用redirect或forword方式進行重定向。究竟采用redirect還是forword取決于forward元素的redirect屬性值。
擴展RequestProcessor
如果不想利用Struts提供的處理器,則可以擴展它。通過兩個步驟即可實現:
1、? 創建一個新的類,該類必須是org.apache.struts.action.RequestProcessor的子類;
2、? 在Struts配置文件中進行聲明,例如:(粗體部分為你的自定義處理器類)
<controller
? contentType="text/html;charset=UTF-8"
? debug="3"
? locale="true"
? nocache="true"
? processorClass="com.struts.framework.CustomRequestProcessor"/>
1.1.1.1??? Action類
如果說ActionServlet是Struts框架的入口,RequestProcessor是消化過濾系統,則org.apache.struts.action.Action類可以說是整個框架的心臟。他是客戶請求和業務操作的連接橋,也可以將其看作是業務操作的客戶代理。
在前面對ReqeustProcessor類的學習中,我們了解到一旦確定并得到了一個action實例,ReqeustProcessor會調用action的execute()方法處理客戶請求,你需要擴展action類,并實現它的execute()方法,在此方法中添加你自己的處理代碼。下面給出是一個示例,這個action用來處理用戶的登錄請求:
package com.oreilly.struts.storefront.security;
?
import java.util.Locale;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import com.oreilly.struts.storefront.customer.view.UserView;
import com.oreilly.struts.storefront.framework.exceptions.BaseException;
import com.oreilly.struts.storefront.framework.UserContainer;
import com.oreilly.struts.storefront.framework.StorefrontBaseAction;
import com.oreilly.struts.storefront.framework.util.IConstants;
import com.oreilly.struts.storefront.service.IStorefrontService;
?
/**
?* Implements the logic to authenticate a user for the Storefront application.
?*/
public class LoginAction extends StorefrontBaseAction {
? /**
?? * Called by the controller when the user attempts to log in to the
?? * Storefront application.
?? */
? public ActionForward execute( ActionMapping mapping,
??????????????????????????????? ActionForm form,
??????????????????????????????? HttpServletRequest request,
??????????????????????????????? HttpServletResponse response )
? throws Exception{
?
??? // The email and password should have already been validated by the ActionForm
??? String email = ((LoginForm)form).getEmail(? );
??? String password = ((LoginForm)form).getPassword(? );
?
??? // Log in through the security service
??? IStorefrontService serviceImpl = getStorefrontService(? );
??? UserView userView = serviceImpl.authenticate(email, password);
?
??? // Create a single container object to store user data
??? UserContainer existingContainer = null;
??? HttpSession session = request.getSession(false);
??? if ( session != null ){
????? existingContainer = getUserContainer(request);
????? session.invalidate(? );
??? }else{
????? existingContainer = new UserContainer(? );
??? }
?
??? // Create a new session for the user
??? session = request.getSession(true);
?
??? // Store the UserView in the container and store the container in the session
??? existingContainer.setUserView(userView);
??? session.setAttribute(IConstants.USER_CONTAINER_KEY, existingContainer);
?
??? // Return a Success forward
??? return mapping.findForward(IConstants.SUCCESS_KEY);
? }
}
?
1.1.1.1.1????? Action類緩沖
Action類被設計為線程安全的,在每個應用中每個Action類只會被實例化一次,供所有線程共享。RequestProcessor利用一個HashMap用來保存Action實例。
思考題?
所有線程共享一個Action類實例意味著什么?我們在編程中需要注意些什么呢?
?
1.1.1.2??? ActionForward類
從前面的介紹我們已經了解到,Action的execute( )方法返回一個ActionForward對象。ActionForward對象是JSP頁面、Java servlet等web資源的抽象表現。
ActionForward的用途是為了減少應用和物理資源(JSP頁面,Java servlet)的耦合,物理資源只需要在配置文件中指定(利用name,path屬性和forward元素的redirect屬性),而不是在代碼中指定。RequestDispatcher利用ActionForward來執行重定向操作。
要在Action中返回一個ActionForward對象,你可以動態地創建一個ActionForward 對象,不過更為通用的解決方案是,通過在Struts配置文件中進行action映射,然后通過關鍵字去查找一個ActionForward 。下面是代碼示例:
? return mapping.findForward( "Success" );
上面的代碼中,"Success"作為參數被傳遞到ActionMapping的findFoward( )方法中,findFoward( )方法在Struts配置文件的global-forwards區域,以及被調用的action的forward元素中查找名字和"Success"相匹配的元素。下面是action元素中的forward示例:
<action
?? input="/security/signin.jsp"
?? name="loginForm"
?? path="/signin"
?? scope="request"
?? type="com.oreilly.struts.storefront.security.LoginAction"
?? validate="true">
?? <forward name="Success" path="/index.jsp" redirect="true"/>
?? <forward name="Failure" path="/security/signin.jsp" redirect="true"/>
?</action>
1.1.1.1??? Action和業務邏輯
思考題?
Action屬于MVC中的Controller還是Model?為什么?
?
1.1.1.2??? 使用Struts內置的Action
Struts1.1框架的org.apache.struts.actions包中包含了5個內置的Action,用來執行一些通用的操作,你可以把它們用在你的項目中,以節省你的開發時間。接下來我們分別介紹這5個內置的Action。
1.1.1.2.1????? org.apache.struts.actions.ForwardAction類
很多情況下,你僅僅需要引導客戶從一個JSP頁面跳轉到另外一個JSP頁面,按照我們通常的做法,可以做一個鏈接讓用戶直接訪問要跳轉到的頁面。但是MVC模式不推薦你這么做,因為,Controller的職責就是接收所有的客戶請求,然后將客戶請求提交給一個合適的模塊進行處理,并將合適的UI推給用戶,如果直接方式JSP頁面,則跳過了Controller的控制,則無法享受Controller所提供的優點。為了解決這個問題,并且不用你去為了執行一個簡單的重定向操作而創建一個Action類 ,Struts框架提供了ForwardAction類,這個Action只是簡單地執行一個重定向操作,重定向的目的地通過parameter屬性配置。要使用ForwardAction類,只需要在Struts配置文件中將Action的type屬性配置為org.apache.struts.actions.ForwardAction:
<action
? input="/index.jsp"
? name="loginForm"
? path="/viewsignin"
? parameter="/security/signin.jsp"
? scope="request"
? type="org.apache.struts.actions.ForwardAction"
? validate="false"/>
</action>
當你訪問/viewsignin的時候,就會自動重定向到/security/signin.jsp。
1.1.1.2.2????? org.apache.struts.actions.IncludeAction類
暫略
1.1.1.2.3????? org.apache.struts.actions.DispatchAction類
暫略
1.1.1.2.4????? org.apache.struts.actions.LookupDispatchAction類
暫略
1.1.1.2.5????? org.apache.struts.actions.SwitchAction類
暫略
1.1.2?????????? Model
Struts沒有定義具體的Model層的實現,Model層通常是和業務邏輯緊密相關的,還通常有持續化的要求,Struts目前沒有考慮到這一層,但是,不管在開源世界還是商業領域,都有一些都別優秀的工具可以為Model層次的開發提供便利,例如優秀的O/R Mapping開源框架Hibernate。
1.1.3?????????? View
通常,Web應用的UI由以下文件組成:
????????? HTML
????????? JSP
而JSP中通常包含以下組件:
????????? 自定義標簽
????????? DTO(Data Transfer Object數據傳輸對象)
在Struts中,還包含了以下兩種常用的組件:
????????? Struts ActionForms
????????? 資源綁定(java resource bundles),例如將標簽的顯示內容,錯誤提示的內容通過配置文件來配置,這樣可以為實現國際化提供基礎。
由此可見,Struts對于傳統的Web UI所作的擴充就是Struts ActionForms和資源綁定,接下來對其進行進一步描述。
1.1.3.1??? 使用 Struts ActionForm
在Struts框架中,ActionForm負責在用戶和業務邏輯層之間來回地傳遞用戶輸入的數據。框架會自動收集用戶輸入并以form bean的方式將這些數據傳遞給Action,然后,form bean可以被傳遞到業務層。不過,為了減少表示層和業務層的耦合,不建議將ActionForm 直接傳遞給業務層,而建議代之為DTO。即在Action中利用form bean的數據創建合適的DTO,然后傳遞給業務層。下面的步驟描述了Struts框架在每一次請求中,是如何處理ActionForm的:
1、???? 檢查是否已經配置ActionForm映射到Action;
2、???? 如果某一個ActionForm被映射到Action,利用配置文件中action元素的name屬性查找相匹配的ActionForm配置信息;
3、???? 檢查是否已經存在該ActionForm的實例(instance);
4、???? 如果存在該ActionForm的實例,并且符合當前請求的需要,則重用這個實例;
5、???? 否則,創建該ActionForm的實例,并且將其保存在合適的生存區域中(生存區域 (scope)的設置請查看action元素,scope表示該實例的生存期限,一般來說,有request,session,application等幾種);
6、???? 調用ActionForm實例的reset()方法;
7、???? 遍歷請求參數,根據不同的參數名,調用ActionForm實例和參數名相對應的setter方法,設置參數值到ActionForm實例中;
8、? 最后,如果validate屬性設置為true,則調用ActionForm實例的validate()方法,該方法可以返回任何錯誤,主要為校驗錯誤。
對于每一個需要傳遞form數據的HTML頁面,必須使用一個ActionForm,同一個ActionForm可以被多個不同的頁面使用,只要HTMLFORM域和ActionForm的屬性相匹配即可。
下面是一個ActionForm的示例:
package com.oreilly.struts.banking.form;
?
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;
/**
?* This ActionForm is used by the online banking appliation to validate
?* that the user has entered an accessNumber and a pinNumber. If one or
?* both of the fields are empty when validate( ) is called by the
?* ActionServlet, error messages are created.
?*/
public class LoginForm extends ActionForm {
? // The user's private ID number
? private String pinNumber;
? // The user's access number
? private String accessNumber;
?
? public LoginForm(? ) {
??? super(? );
??? resetFields(? );
? }
? /**
?? * Called by the framework to validate the user has entered values in the
?? * accessNumber and pinNumber fields.
?? */
? public ActionErrors validate(ActionMapping mapping, HttpServletRequest req ){
??? ActionErrors errors = new ActionErrors(? );
?
??? // Get access to the message resources for this application.
??? // There's no easy way to access the resources from an ActionForm.
??? MessageResources resources =
????? (MessageResources)req.getAttribute( Action.MESSAGES_KEY );
?
??? // Check and see if the access number is missing.
??? if(accessNumber == null || accessNumber.length(? ) == 0) {
????? String accessNumberLabel = resources.getMessage( "label.accessnumber" );
????? ActionError newError =
??????? new ActionError("global.error.login.requiredfield", accessNumberLabel );
????? errors.add(ActionErrors.GLOBAL_ERROR, newError);
??? }
?
??? // Check and see if the pin number is missing.
??? if(pinNumber == null || pinNumber.length(? ) == 0) {
????? String pinNumberLabel = resources.getMessage( "label.pinnumber" );
????? ActionError newError =
??????? new ActionError("global.error.login.requiredfield", pinNumberLabel );
????? errors.add(ActionErrors.GLOBAL_ERROR, newError);
??? }
??? // Return the ActionErrors, if any.
??? return errors;
? }
?
? /**
?? * Called by the framework to reset the fields back to their default values.
?? */
? public void reset(ActionMapping mapping, HttpServletRequest request) {
??? // Clear out the accessNumber and pinNumber fields.
??? resetFields(? );
? }
? /**
?? * Reset the fields back to their defaults.
?? */
? protected void resetFields(? ) {
??? this.accessNumber = "";
??? this.pinNumber = "";
? }
?
? public void setAccessNumber(String nbr) {
??? this.accessNumber = nbr;
? }
?
? public String getAccessNumber(? ) {
??? return this.accessNumber;
? }
?
? public String getPinNumber(? ) {
??? return this.pinNumber;
? }
? public void setPinNumber(String nbr) {
??? this.pinNumber = nbr;
? }
}
Struts框架提供的ActionForm實現了一些方法,到現在為止,最重要的兩個方法是reset()和validator():
public void reset( ActionMapping mapping, HttpServletRequest request );
public ActionErrors validate( ActionMapping mapping, HttpServletRequest request );
?
ActionForm對于這兩個方法的默認實現是不執行任何操作,你可以重載這兩個方法來執行具體的邏輯。
當寫好了ActionForm類之后,需要通知Struts應用程序它的存在,以及action和ActionForm之間的映射關系。在Struts中,是通過配置文件來實現這一目的的。第一步,將應用所需要用到的所有ActionForm在配置文件的form-beans這一段加以描述,下面的片斷描述了如何通知Struts應用程序這三個ActionForm的存在。
<form-beans>
?<form-bean
?? name="loginForm"
?? type="com.oreilly.struts.banking.form.LoginForm"/>?
?<form-bean
?? name="accountInformationForm"
?? type="org.apache.struts.action.DynaActionForm">
?? <form-property name="accounts" type="java.util.ArrayList"/>
?</form-bean>?
?<form-bean
?? name="accountDetailForm"
?? type="org.apache.struts.action.DynaActionForm">
?? <form-property
???? name="view"
???? type="com.oreilly.struts.banking.view.AccountDetailView"/>
?</form-bean>???
</form-beans>
每一個form bean的name屬性必須唯一,type屬性定義了該form bean的類,該類必須實現Struts ActionForm類。下一步就是建立action和form-bean的聯系,form-bean可以和一到多個action建立聯系,通過在action元素的屬性中引用form-bean的name即可完成映射,下面的片斷顯示了LoginAction和form-bean的映射,我們之前已經看過這個片段:
<action
? path="/login"
? type="com.oreilly.struts.banking.action.LoginAction"
? scope="request"
? name="loginForm"
? validate="true"
? input="/login.jsp">
? <forward name="Success" path="/action/getaccountinformation" redirect="true"/>
? <forward name="Failure" path="/login.jsp" redirect="true"/>
</action>
?
在Struts1.1中,添加了一種新類型的action form ,叫做org.apache.struts.action.DynaActionForm,這種類型的action form可以配置為action的映射,它會自動處理HTML form中的數據并將其傳遞到Action。DynaActionForm 如何做到自動處理HTML form數據的呢?DynaActionForm內部使用一個Map來存放HTML field數據。
在接下來的一節中,我們詳細了解一下DynaActionForm。
1.1.1.1??? 使用DynaActionForm
從上一節的介紹,我們可以看出,使用ActionForm 和我們自己來編寫類獲取HTML from值,在進行處理相比,有不少優勢。ActionForm所封裝的數據和行為時幾乎每一個web 應用程序都需要的,而且在一個應用中會多次用到,例如一個信息實體的增加和修改,可能從不同的角度,不同的頁面實現信息實體的增、改,通過ActionForm就可以復用,復用可以統一規則,減少開發時間和維護工作量。但是,現在對ActionForm的使用越來越少,為什么呢?
第一,?????????? 也是一個最大的問題,會使得項目中存在很多ActionForm類,增加了整個項目類的數目和維護復雜度,有的開發人員為了避開這個問題,使用一個很大的,包含所有HTML from屬性的ActionForm 來和所有action映射,這種方式我認為問題更多,完全失去了封裝的味道。
第二,?????????? 當需要添加或者刪除一個HTML from 屬性時,如果ActionForm 需要用到這些屬性,就得修改ActionForm ,并且要重新編譯。
基于這些原因,在Struts1.1框架中,添加了一種新類型的ActionForm,這種ActionForm可以動態變化從而避免創建具體的ActionForm類。這種ActionForm的基類是org.apache.struts.action.DynaActionForm,當然,DynaActionForm是從ActionForm擴展而來的。對于應用來說,DynaActionForm和ActionForm在以下三個方面會有些不同:
????????? ActionForm的屬性定義
????????? validate()方法
????????? reset()方法
DynaActionForm的屬性不同于ActionForm,ActionForm的屬性是通過具體的類,具體的setter,getter方法來進行屬性值的設置,獲取,而DynaActionForm是在Struts的配置文件中定義的。
對reset()方法調用的時機和ActionForm并無不同,只是相對來說,在reset()方法被調用的時候,你擁有比較少的控制權。當然,可以通過擴展DynaActionForm,重載reset()方法。
對于從UI端輸入的數據的校驗,相對來說有些復雜,它是通過Struts Validator組件來實現的,稍后會詳細介紹它。
1.1.1.1.1????? 配置DynaActionForm
要使用DynaActionForm,首先得在Struts配置文件中添加form-bean元素。在配置文件中,DynaActionForm和ActionForm的不同之處在于,DynaActionForm需要添加一些form-property元素,form-property用來指定HTML form中的field名字,Struts框架會通過這些名字的匹配,自動將HTML form 各個field的值封裝到DynaActionForm實例中。下面的片斷是關于DynaActionForm的配置文件示例 :
<form-beans>
? <form-bean
??? name="loginForm"
??? type="org.apache.struts.action.DynaActionForm">
?
??? <!-在下面制定Form的屬性 -->
??? <form-property
????? name="email"
????? type="java.lang.String "/>
??? <form-property
????? name="password"
????? type="java.lang.String "/>
?
??? <!-可以為屬性設置默認值 -->
??? <form-property
????? initial="false"
????? name="rememberMe"
????? type="java.lang.Boolean "/>
? </form-bean>
<form-beans>
當你HTML form中添加了一個屬性,需要在DynaActionForm中添加一個屬性時,就不需要去修改具體的ActionForm類,只需要在配置文件中添加一個form-property元素即可,大大提高了可擴展能力。
前面我們已經了解到,ActionForm的reset()方法默認不進行任何操作,在DynaActionForm中,reset()方法默認將所有屬性設置為默認值,如果在配置文件中沒有為該屬性設置默認值,將會按照java編程語言的規范根據屬性的類型為其進行初始化,例如:數字(int ,double,float)的將會初始化為0,Ojbect類型將為初始化為null。
注意:在配置文件中定義的form-property的type屬性,其值為一個java類名,因此對于java語言中的主類型,如ini,long必須定義為java.lang.Int,java.lang.Long,其它主類型依次類推。
1.1.1.1.1????? 使用DynaActionForm執行校驗規則
?同ActionForm一樣,DynaActionForm也沒有提供validate()方法的默認操作,幸運的是,Struts提供了另外一種框架來幫助大家解決校驗的問題,這就是Struts Validator框架。Struts Validator 框架由David Winterfeldt編寫,現在已經成為Struts主分發的一部分,它提供基于規則的校驗,對于常用的校驗,如必填項目,email,電話等等提供了現成的規則,只需要通過配置文件進行配置即可。關于Struts Validator框架的詳細介紹,見后續章節。
1.1.1.2???? Validator 框架
Struts允許在ActionForm的validator()方法中添加校驗代碼,對用戶輸入的數據進行規則校驗,這能很好地工作,但是,這存在一些限制,舉個簡單的例子,一個必填項現在不是必填項了,要滿足這個簡單的需求就需要更改ActionForm類的validator()方法,再進行重新編譯,很麻煩。本節我們來學習Validator框架,看它是如何解決校驗問題的。
1.1.1.2.1????? Validator的安裝、配置
Validator目前是Jakarta Commons 項目的一部分,它也被包含在Struts主分發里面,可以直接使用Struts中自帶的Validator庫,也可以去網站上下載http://jakarta.apache.org/commons/。
Validator需要一些其它庫的支持,例如Jakarta ORO,不過沒關系,Struts分發包里面都包含了,你只需要按照第2章介紹的將Struts安裝到你的應用中,就一切ok。
1.1.1.2.1.1??????????? 配置校驗規則
前面提到過,Validator是通過校驗規則來實施校驗,這些校驗規則被配置在配置文件中,這意味著不需要修改源代碼就可以方便地更改校驗規則,是不是感覺很不錯?Validator所需要的配置文件有兩個:validation-rules.xml和validation.xml。
1.1.1.2.1.2??????????? validation-rules.xml
在validation-rules.xml文件中配置了一些全局性的校驗規則,使得你在應用程序中使用校驗而不用關注實現細節。這個配置文件是Validator框架自帶的,可以用在所有Struts應用中。它默認配置了許多很常用的規則,一般來說,不用去更改它,除非需要擴展或修改這些默認的校驗規則。
建議:即使你需要擴展一些規則,也不要去修改validation-rules.xml,而是通過新的配置文件去定義你所擴展的校驗規則。
validator-rules_1_1.dtd定義了validation-rules.xml文件的結構,根元素是form-validation,它包含一到多個global元素,global元素包含一到多個validator元素。
每一個validator元素定義了一個唯一的校驗規則。下面是validation-rules.xml文件中的一個片斷,用來定義必填項(required)校驗規則:
<validator
? name="required"
? classname="org.apache.struts.util.StrutsValidator"
? method="validateRequired"
? methodParams="java.lang.Object,
??? org.apache.commons.validator.ValidatorAction,
??? org.apache.commons.validator.Field,
??? org.apache.struts.action.ActionErrors,
??? javax.servlet.http.HttpServletRequest"
? msg="errors.required">
</validator>
下表詳細介紹了validator元素每個屬性的具體含義:
序號?屬性?解釋
1.??????? ?name?賦予校驗規則一個唯一的名稱,便于在validation-rules.xml文件和應用指定的其它校驗文件中引用。
2.??????? ?classname?指定含有具體校驗規則Java Class名,org.apache.struts.util.StrutsValidator是Validator框架自帶的一個Java Class,它實現了一些很常用的校驗規則。
3.??????? ?method?指定含有具體校驗規則Java Class的具體方法,一個校驗規則有實現校驗的Java Class的一個方法來實現。
4.??????? ?methodParams?聲明method屬性所指定的方法的參數,參數之間用逗號分隔。
5.??????? ?msg?msg是用來指定當校驗不通過時,Validator框架所給出的提示信息。它的值是應用所配置的資源文件中的一個關鍵字,當校驗失敗時,Validator框架利用msg所指定的值到應用配置的資源文件中去查找匹配記錄。Validator框架默認使用以下提示信息:
?? errors.required={0} is required.
?? errors.minlength={0} cannot be less than {1} characters.
?? errors.maxlength={0} cannot be greater than {1} characters.
?? errors.invalid={0} is invalid.??
?? errors.byte={0} must be a byte.
?? errors.short={0} must be a short.
?? errors.integer={0} must be an integer.
?? errors.long={0} must be a long.
?? errors.float={0} must be a float.
?? errors.double={0} must be a double.??
?? errors.date={0} is not a date.
?? errors.range={0} is not in the range {1} through {2}.
?? errors.creditcard={0} is not a valid credit card number.
?? errors.email={0} is an invalid email address
可以將上面的這些信息添加到你的Struts應用所配置的資源文件(例如:ApplicationResources.properties)中,也可以修改這些值之后,將其添加到配置文件中,示例如下:
?errors.required={0} 是必填項。
6.??????? ?depends?depends指定在本校驗規則的前置校驗規則,下面的片斷定義了一個最小長度的校驗規則,含義是在進行最小長度校驗之前,會先調用required校驗規則確保數據不為空:
<validator
? name="minLength"
? classname="org.apache.struts.util.StrutsValidator"
? method="validateMinLength"
? methodParams="java.lang.Object,
??? org.apache.commons.validator.ValidatorAction,
??? org.apache.commons.validator.Field,
??? org.apache.struts.action.ActionErrors,
??? javax.servlet.http.HttpServletRequest"
? depends="required"
? msg="errors.minlength">
</validator>
如果存在多個前置校驗規則,則可以用以下的方式進行聲明,各校驗規則之間用逗號分隔:
depends="required,integer"
如果前置校驗規則失敗,則后續的校驗規則不會被執行。
7.??????? ?jsFunctionName?可選屬性。用來指定JavaScript函數的名字。
The final attribute supported by the validator element is the jsFunctionName attribute. This optional attribute allows you to specify the name of the JavaScript function. By default, the Validator action name is used.
?
前面已經介紹了,org.apache.struts.util.StrutsValidator是Validator框架自帶的一個校驗規則類,其實現了一些常用的校驗規則,其包含的校驗方法(method)如下所列:
?"?????????? validateByte? 檢查值能夠安全地轉換為byte
"???????? validateCreditCard? 檢查值是一個有效的信用卡號碼
"???????? validateDate 檢查值是一個有效的日期
"???????? validateDouble檢查值能夠安全地轉換為double
"???????? validateEmail? 檢查值是一個有效的Email地址
"???????? validateFloat? 檢查值能夠安全地轉換為double
"???????? validateInteger? 檢查值能夠安全地轉換為int
"???????? validateLong檢查值能夠安全地轉換為long
"???????? validateMask檢查值符合掩碼規則,掩碼采用規則表達式的方式
"???????? validateMinLength 檢查值的長度大于等于指定長度
"???????? validateMaxLength檢查值的長度小于指定長度
"???????? validateRange檢查值的有效范圍在指定范圍內
"???????? validateRequired檢查值不為null或長度>0
"???????? validateShort 檢查值能夠安全地轉換為short
1.1.1.1.1.1??????????? validation.xml
Validator框架所需要的第二個配置文件是validation.xml,這個配置文件是具體應用(項目)所特定的,可以根據你的應用(項目)情況進行自定義配置。它描述了具體的ActionForm使用validation-rules.xml文件中的哪個校驗規則進行校驗。
validation_1_1.dtd定義了validation.xml的結構,根元素為form-validation,其包含0到多個global元素和一到多個formset元素:
<!ELEMENT form-validation (global*, formset+)>
global元素包含0到多個constant子元素:
<!ELEMENT global (constant*)>
constant子元素和Java里面常量的含義是一樣的,下面的片斷定義了兩個常量:
<global>
?<constant>
? <constant-name>phone</constant-name>
? <constant-value>^\(?(\d{3})\)?[-| ]?(\d{3})[-| ]?(\d{4})$</constant-value>
?</constant>
?<constant>
? <constant-name>zip</constant-name>
? <constant-value>^\d{5}(-\d{4})?$</constant-value>
?</constant>
</global>
上面的片斷包含了兩個常量,phone 和zip,這些常量在所有formset元素中有效,在formset中通過名稱引用這些常量。
下面的片斷展示了一個簡單的validation.xml文件說明:
代碼片斷 3.3.3.3.1.3.1
<form-validation>
?<global>
? <constant>
?? <constant-name>phone</constant-name>
?? <constant-value>^\(?(\d{3})\)?[-| ]?(\d{3})[-| ]?(\d{4})$</constant-value>
? </constant>????????????????
?</global>
?<formset>??
? <form name="checkoutForm">
?? <field
???? property="phone"
???? depends="required,mask">
???? <arg0 key="registrationForm.firstname.displayname"/>
???? <var>
?????? <var-name>mask</var-name>
?????? <var-value>${phone}</var-value>
???? </var>????
??? </field>???
?? </form>???????????
? </formset>??
</form-validation>
在上面的代碼片斷中,var元素應用了在global中定義了phone常量,用來配合對phone屬性的校驗。
formset元素可以包含兩個子元素,constant和form。constant元素和global區域定義的constant元素格式和用途一樣,只不過作用范圍不同,在formset中定義的constant元素其作用范圍只限于該formset覆蓋區域。Formset元素中的form元素至少要出現一次。DTD描述如下:
<!ELEMENT formset (constant*, form+)>
form元素定義了需要進行校驗的域,其name屬性對應應用中分配給form的標識,在Struts框架中,就是在Struts配置文件中form-beans區域定義的ActionForm的name屬性。
下面是form元素的DTD定義:
<!ELEMENT form (field+)>
field元素指明了JavaBean中需要被校驗的屬性。在上面的代碼片斷中,在Struts中,ActionForm就是這個需要被校驗的JavaBean。在代碼片斷 3.3.3.3.1.3.1中,定義了對Struts配置文件中名稱為checkoutForm的ActionForm所擁有的名稱為phone的屬性的校驗說明,表示checkoutForm的phone屬性為必填項而且符合${phone}所定義的正則表達式的掩碼規則。field元素的屬性在下表中具體描述:
屬性?描述
property?JavaBean(在Struts為ActionForm)中需要被校驗的屬性的名稱。
depends?應用于property指定屬性的校驗規則列表,多個校驗規則之間用逗號分隔。
page?這個屬性在應用于"向導"模式的form中,用來確保不會跳頁訪問。
indexedListProperty?不會用
表3.3.3.3.1.3.1 field元素的屬性列表
field元素包含以下幾個子元素,DTD定義如下:
<!ELEMENT field (msg?, arg0?, arg1?, arg2?, arg3?, var*)>
msg子元素允許你為該field指定一個提示信息,校驗規則將會使用這個指定的提示信息替代規則默認的提示信息,msg子元素的值必須是應用資源文件的一個關鍵字(key)。例如:
<field property="phone" depends="required,mask">
? <msg name="mask" key="phone.invalidformat"/>
? <arg0 key="registrationForm.firstname.displayname"/>
? <var>
??? <var-name>mask</var-name>
??? <var-value>${phone}</var-value>
? </var>??????????????
</field>
msg子元素支持三個屬性,DTD定義如下:
<!ATTLIST msg name???? CDATA #IMPLIED
???????????????? key????? CDATA #IMPLIED
???????????????? resource CDATA #IMPLIED >
name 屬性指定了msg將使用的校驗規則名稱,屬性值必須是在validation-rules.xml 文件中定義的校驗規則。
key 屬性指定了一個資源文件的關鍵字,當校驗失敗是,該關鍵字所代表的信息將會添加到ActionError中。如果你想設置一個明確的文本而不是資源文件的關鍵字,則可以將resource屬性設置位false,這種情況下,可以將key屬性設置為一個明確的文本,如"電話格式不正確!"。
?field元素可以包含至多四個額外的子元素,它們分別命名為arg0, arg1, arg2和arg3,它們用來向提示信息中添加額外的信息,arg0定義了第一個可替換的值,arg1定義了第二個可替換的值,以此類推。每個arg元素支持三個屬性,name, key和resource,其含義和之前我們看到的msg元素的同名屬性含義一致。下面是一段應用arg元素的示例:
<field property="phone" depends="required,mask,minLength">
? <arg0 key="registrationForm.firstname.displayname"/>
? <arg1 name="minlength" key="${var:minLength}" resource="false"/>
? <var>
?? <var-name>mask</var-name>
?? <var-value>${phone}</var-value>
? </var>????
? <var>
?? <var-name>minLength</var-name>
?? <var-value>5</var-value>
? </var>????????
</field>?
field元素包含的最后一個子元素是var元素,field元素可以包含0到多個var元素,var 元素可以設置該field所用到的校驗規則的參數,var-name參數名,var-value指定參數值。例如設置mask規則的具體的正則表達式:
?<var-name>mask</var-name>
?<var-value>${phone}</var-value>
設置minLength規則的最小長度:
<var-name>minLength</var-name>
<var-value>5</var-value>
當然,這個參數可以被arg元素應用,應用語法為:${var:var-name}。
1.1.1.1.1.1??????????? 插入Validator
每一個Struts應用需要知道是否使用了Validator框架,可以通過PlugIn(插件)機制將Validator框架配置到Struts應用中。
下面的代碼演示了如何將Validator作為一個插件加入到Struts應用中,在Struts應用的配置文件Struts-config.xml中加入如下代碼片段:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
? <set-property
??? property="pathnames"
??? value="/WEB-INF/validator-rules.xml,/WEB-INF/validator.xml"/>
</plug-in>
粗體部分pathnames屬性的值用來指定Validator框架所使用的配置文件,多個配置文件之間用逗號分隔。
當應用啟動的時候,Struts框架將調用ValidatorPlugIn的init()方法。Validator框架的配置文件將會加載到內存中供應用使用。在init()方法被調用之前,pathnames所指定的值將會傳遞給ValidatorPlugIn的實例,ValidatorPlugIn實例將會依據這個值去加載配置文件。
1.1.1.2??? 使用帶校驗的ActionForm
你不能使用標準的Struts ActionForm去和Validator配合使用。你必須使用專門為Validator框架設計的ActionForm的子類。現在有兩個子類可以選擇,取決于你是否打算使用動態ActionForms。下面的圖直觀地顯示了ActionForm以及它的后代:
?
如果你打算使用動態ActionForm,為了和Validator框架配合使用,你可以使用DynaValidatorForm,否則,可以使用ValidatorForm。從圖上看出,DynaValidatorForm有個子類叫做DynaValidatorActionForm,ValidatorForm有個子類叫做ValidatorActionForm,這兩個子類在其父類的名字中間加了個"Action",這兩個類有什么作用呢?
同樣,根據你是否打算使用動態ActionForm,你可以使用DynaValidatorActionForm或ValidatorActionForm,來配合使用Validator框架,當使用這兩個類時,它們將action的path屬性傳遞給Validator,Validator使用action的名字去查找使用的校驗規則。而使用DynaValidatorForm和ValidatorForm,則是使用的ActionForm 的name屬性去查找匹配校驗規則。(???)
1.1.1.1.1????? 示例
第一步,在Struts-config.xml中配置一個ActionForm,示例如下:
<form-bean
? name="checkoutForm"
? type="org.apache.struts.validator.DynaValidatorForm">
? <form-property name="firstName" type="java.lang.String"/>
? <form-property name="lastName" type="java.lang.String"/>
? <form-property name="address" type="java.lang.String"/>???
? <form-property name="city" type="java.lang.String"/>
? <form-property name="state" type="java.lang.String"/>
? <form-property name="postalCode" type="java.lang.String"/>
? <form-property name="country" type="java.lang.String"/>
? <form-property name="phone" type="java.lang.String"/>???
</form-bean>
第二步,在Struts-config.xml中配置一個Action,示例如下:
<action
?? input="/checkout.jsp"
?? name="checkoutForm"
?? path="/checkout"
?? scope="request"
?? type="com.ort.struts.example.checkOutAction"
?? validate="true">
?</action>
?
第三布,在validation.xml文件中定義如下:
<formset>
? <constant>
??? <constant-name>phone</constant-name>
??? <constant-value>^\(?(\d{3})\)?[-| ]?(\d{3})[-| ]?(\d{4})$</constant-value>
? </constant>????????????????
? <constant>
??? <constant-name>zip</constant-name>
??? <constant-value>^\d{5}(-\d{4})?$</constant-value>
? </constant>??????????????????????
? <form name="checkoutForm">
??? <field
????? property="firstName"
????? depends="required,mask">
????? <arg0 key="label.firstName"/>??????????
????? <var>
??????? <var-name>mask</var-name>
??????? <var-value>^[a-zA-Z]*$</var-value>
????? </var>?????????????????
??? </field>
??? <field
????? property="postalCode"
????? depends="required,mask">
????? <arg0 key="registrationForm.zip"/>
????? <var>
??????? <var-name>mask</var-name>
??????? <var-value>${zip}</var-value>
????? </var>
??? </field>
??? <field
????? property="phone"
????? depends="required,mask">
????? <arg0 key="registrationForm.phone"/>
????? <var>
??????? <var-name>mask</var-name>
??????? <var-value>${phone}</var-value>
????? </var>
??? </field>????????
? </form>???????????
?</formset>??
</form-validation>
第四部,編寫HTML頁面如下:
暫略
?
1.???? JSP 自定義標簽庫
1.1? 概述
在JSP開發中會遇到一些重復的工作。而使用自定義標簽庫是一種方法,可以用來將這些功能封裝起來并在多個項目中重新用到它。此外,應用邏輯還可以包含在基于服務器的資源中,比如JavaBeans。這種架構顯示出使用自定義標簽庫可以更快更容易地開發基于Web的應用程序。
有關JavaBeans和自定義標簽庫的最初想法是:在程序員研究那些包含商務邏輯(business logic)的類的同時,Web設計師可以同步進行頁面設計。然后,Web設計師可以通過使用簡單的"連線"將JSP頁面和這些類聯系起來。盡管使用JavaBean會減少在JSP頁面中寫代碼的數量,但你還是得寫程序去使用這些Beans。
然而使用自定義標簽庫則是一種完全無需在JSP中寫代碼的好辦法。這并不是說自定義標簽庫會取代JavaBeans,它們都是用來分離實際內容和顯示形式的。JavaBeans在用于商務邏輯被重用的設計中作用更為明顯。JavaBeans通常能在不同項目的各種頁面中被用到。另一方面,自定義標簽庫則是一個特殊頁面的自定義形式,即便如此,將它重新利用到其他程序中也是很常見的。得到自定義標簽庫的一種方法是自己建一個。但為什么不使用現成的呢?比如Jakarta Taglibs項目(源自Apache Software Foundation)就提供了一些自定義標簽庫,它們可以在不同的JSP應用程序中重復使用。
1.2? Struts包含的標簽庫
Struts框架提供了一系列的框架組件,同時,他也提供了一系列的標簽(Tag)用于和框架進行交互。Struts提供的標簽包含在以下四個標簽庫(Tag libraries)中:
"???????? HTML
"???????? Bean
"???????? Logic
"???????? Template
這四個標簽庫所包含的標簽功能各自截然不同,從標簽庫的名字我們可以看出其功能,如,HTML標簽庫是用來包裝HTML控件的。
1.3? 在Struts應用中使用標簽庫
和使用其它標簽庫一樣,使用Struts提供的標簽庫只需要簡單的兩步:
1、? 在web.xml中聲明標簽庫:
<taglib>
? <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
?
<taglib>
? <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
? <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
?
2、? 在JSP頁面中引入標簽庫:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
?
1.4? Struts HTML標簽庫
HTML標簽庫中的標簽列表
標簽名?描述
base?包裝HTML的base元素
button?包裝HTML的 button類型的input元素
cancel?包裝HTML cancel 按鈕
checkbox?包裝HTML checkbox 類型的輸入域
errors?有條件地顯示一些error消息,顯示ActionErrors信息
file?包裝HTML文件上傳輸入域
form?定義HTML form 元素
frame?包裝HTML frame 元素
hidden?包裝HTML hidden 輸入域
html?包裝 HTML中的 html 元素
image?包裝 "image"類型的輸入域
img?包裝HTML的 img 元素
javascript?包裝根據ValidatorPlugIn提供的校驗規則所提供的javascript校驗腳本
link?包裝超鏈接
messages?有條件地顯示一些提示信息,顯示ActionMessages信息
multibox?包裝多選輸入框
option?包裝一個選擇輸入框
options?包裝一批選擇輸入框
optionsCollection?包裝一批選擇輸入框集
password?包裝密文輸入框
radio?包裝單選輸入框
reset?包裝"重置"功能的按鈕
rewrite?包裝一個URL
select?包裝一個選擇輸入框
submit?包裝一個提交按鈕
text?包裝一個文本輸入框
textarea?包裝一個備注輸入框
在這里,不打算對每一個標簽的使用進行詳細說明,要想了解每一個標簽的使用,請查看Struts官方文檔。
接下來,我們著重學習一下幾個非常重要的標簽的使用,舉一反三,通過這幾個標簽的使用,我想,即使不去看官方文檔,也能夠對其它標簽的使用有個基本的了解。
1.1.1?????????? form標簽
Struts的form標簽是最重要的標簽之一,他包裝了HTML的標準form標簽,提供了將HTML form和ActionForm 連接起來的功能。
HTML form中的每一個域對應ActionForm的一個屬性,當提交HTML from后,Struts根據匹配關系,將HTML from域的值賦給ActionForm的同名屬性。下表列舉了form標簽的屬性,并且針對每一個屬性加以詳細說明:
Struts form標簽屬性列表
Name?Description
action?form提交的目標地址,action用來選擇一個Struts Action對提交后的客戶請求進行處理。通過和action的path屬性進行匹配來選擇Struts Action。
如果在web.xml中設置的servlet映射是擴展名映射,則可以用以下方式進行Action匹配(擴展名可以不作為匹配內容):
<html:form action="login.do" focus="accessNumber">
上面的語句表示form提交后,交給path屬性值為login的Action進行處理
如果在web.xml中設置的servlet映射是路徑映射,則action的值必須完全匹配Struts Action的path屬性值,例如:
<html:form action="login" focus="accessNumber">
enctype?提交form使用的編碼方式
focus?頁面初始化時光標定位的輸入控件名
method?提交請求的HTTP方式('POST' or 'GET')
name?對應的ActionForm的名字 ,如果不指定,則使用Struts Action在配置文件中name屬性所指定的ActionForm。
onreset?當form重置時執行的Javascript
onsubmit?當form提交時執行的javascript
scope?與form對應的ActionForm的有效區域,可以為request 或session
style?CSS樣式表The CSS styles to be applied to this HTML element.
styleClass?CSS樣式類別
styleId?HTML元素的ID
target?form提交的目標frame
type?form對應的ActionForm的類名
?
1.2? Struts Logic標簽庫
Struts Logic標簽庫中包含的標簽列表
Tag name?Description
empty?如果標簽parameter,propertie等屬性所指定的變量值為null或空字符串,則處理標簽包含的內容
equal?如果標簽parameter,propertie等屬性所指定的變量的值等于標簽value屬性所指定的值,則處理標簽所包含的內容,如:
<logic:equal value="modify" property="action"? name="projectForm">
??? <bean:message key="project.project_modify"/>
</logic:equal>
上面的示例表示,如果projectForm的action屬性等于modify,則處理<bean:message key="project.project_modify"/>語句。
forward?Forward control to the page specified by the ActionForward entry.
greaterEqual?Evaluate the nested body content of this tag if the requested variable is greater than or equal to the specified value.
greaterThan?Evaluate the nested body content of this tag if the requested variable is greater than the specified value.
iterate?Repeat the nested body content of this tag over a specified collection.
lessEqual?Evaluate the nested body content of this tag if the requested variable is less than or equal to the specified value.
lessThan?Evaluate the nested body content of this tag if the requested variable is less than the specified value.
match?Evaluate the nested body content of this tag if the specified value is an appropriate substring of the requested variable.
messagesNotPresent ?Generate the nested body content of this tag if the specified message is not present in this request.
messagesPresent?Generate the nested body content of this tag if the specified message is present in this request.
notEmpty?Evaluate the nested body content of this tag if the requested variable is neither null nor an empty string.
notEqual?Evaluate the nested body content of this tag if the requested variable is not equal to the specified value.
notMatch?Evaluate the nested body content of this tag if the specified value is not an appropriate substring of the requested variable.
notPresent?Generate the nested body content of this tag if the specified value is not present in this request.
present?Generate the nested body content of this tag if the specified value is present in this request.
redirect?Render an HTTP redirect.
?
執行比較功能的標簽通用屬性表
Name?Description
name?The name of a bean to use to compare against the value attribute. If the property attribute is used, the value is compared against the property of the bean, instead of the bean itself.
parameter?The name of a request parameter to compare the value attribute against.
property?The variable to be compared is the property (of the bean specified by the name attribute) specified by this attribute. The property reference can be simple, nested, and/or indexed.
scope?The scope within which to search for the bean named by the name attribute. All scopes will be searched if not specified.
value?The constant value to which the variable, specified by another attribute(s) of this tag, will be compared.
?
示例:
To check whether a particular request parameter is present, you can use the Logic present tag:
<logic:present parameter="id">
? <!-- Print out the request parameter id value -->
</logic:present>
To check whether a collection is empty before iterating over it, you can use the notEmpty tag:
<logic:notEmpty name="userSummary" property="addresses">
? <!-- Iterate and print out the user's addresses -->
</logic:notEmpty>
Finally, here's how to compare a number value against a property within an ActionForm:
<logic:lessThan property="age" value="21">?
? <!-- Display a message about the user's age -->
</logic:lessThan>
1.1? Struts Bean標簽庫
Struts Bean標簽庫中的標簽列表
Tag name?Description
cookie?Define a scripting variable based on the value(s) of the specified request cookie.
define?Define a scripting variable based on the value(s) of the specified bean property.
header?Define a scripting variable based on the value(s) of the specified request header.
include?Load the response from a dynamic application request and make it available as a bean.
message?Render an internationalized message string to the response.
page?Expose a specified item from the page context as a bean.
parameter?Define a scripting variable based on the value(s) of the specified request parameter.
resource?Load a web application resource and make it available as a bean.
size?Define a bean containing the number of elements in a Collection or Map.
struts?Expose a named Struts internal configuration object as a bean.
write?Render the value of the specified bean property.
?
1.2? Struts Template標簽庫
Struts Template標簽庫中的標簽列表
Tag name?Description
insert?Retrieve (or include) the specified template file, and then insert the specified content into the template's layout. By changing the layout defined in the template file, any other file that inserts the template will automatically use the new layout.
put?Create a request-scope bean that specifies the content to be used by the get tag. Content can be printed directly or included from a JSP or HTML file.
get?Retrieve content from a request-scope bean, for use in the template layout.
?
1.???? 示例
示例實現了一個簡單的項目信息CRUD(添加、查詢、更新、刪除)+分頁顯示的功能。
其中項目有如下屬性:
????????? 項目編號
????????? 項目名稱
????????? 項目所屬區域(區域存在與一個字典表中)
????????? 項目分成比例(0.00%--100.00%)
?
數據庫結構定義如下所示(采用Oracle8i數據庫,如果采用其它數據庫請作相應變化):
SITES --區域表
Name?Datatype?Null Option?Comment?Is PK
SITECODE?VARCHAR2(10)?NOT NULL?區域號?Yes
SITENAME?VARCHAR2(30)?NOT NULL?區域名稱?No
?
PROJECTS --項目表
Name?Datatype?Null Option?Comment?Is PK
PROJECTCODE?VARCHAR2(10)?NOT NULL?項目編號?Yes
PROJECTNAME?VARCHAR2(30)?NOT NULL?項目名稱?No
SITECODE?VARCHAR2(10)?NOT NULL?所屬區域號?
DISCOUNT?NUMBER?NULL?項目分成比例?
?
附:在ORACLE8i中創建數據表的語句為:
--創建區域表
CREATE TABLE SITES (
?SITECODE VARCHAR2(10) NOT NULL,
?SITENAME VARCHAR2(30) NOT NULL
);
--添加主鍵
ALTER TABLE SITES
?????? ADD? ( CONSTRAINT SITES_PKSITECODE PRIMARY KEY (SITECODE)
?????? USING INDEX );?
--創建項目信息表
CREATE TABLE PROJECTS(
?PROJECTCODE VARCHAR2(10) NOT NULL,
?PROJECTNAME VARCHAR2(30) NOT NULL,
?SITECODE VARCHAR2(10) NOT NULL,
?DISCOUNT NUMBER NULL
);
--添加主鍵索引
ALTER TABLE PROJECTS
?????? ADD? ( CONSTRAINT PROJECTS_PKPROJECTCODE PRIMARY KEY (PROJECTCODE)
?????? USING INDEX );?
--添加外鍵索引
CREATE INDEX FK_PROJECTSSITECODE ON PROJECTS
(
?????? SITECODE? ASC
);
--添加外鍵約束
?ALTER TABLE PROJECTS
?????? ADD? ( CONSTRAINT FK_PROJECTSSITECODE
????????????? FOREIGN KEY (SITECODE)
???????????????????????????? REFERENCES SITES ) ;
--創建區域表
CREATE TABLE SITES (
?SITECODE VARCHAR2(10) NOT NULL,
?SITENAME VARCHAR2(30) NOT NULL
);
--添加主鍵
ALTER TABLE SITES
?????? ADD? ( CONSTRAINT SITES_PKSITECODE PRIMARY KEY (SITECODE)
?????? USING INDEX );?
--創建項目信息表
CREATE TABLE PROJECTS(
?PROJECTCODE VARCHAR2(10) NOT NULL,
?PROJECTNAME VARCHAR2(30) NOT NULL,
?SITECODE VARCHAR2(10) NOT NULL,
?DISCOUNT NUMBER NULL
);
--添加主鍵索引
ALTER TABLE PROJECTS
?????? ADD? ( CONSTRAINT PROJECTS_PKPROJECTCODE PRIMARY KEY (PROJECTCODE)
?????? USING INDEX );?
--添加外鍵索引
CREATE INDEX FK_PROJECTSSITECODE ON PROJECTS
(
?????? SITECODE? ASC
);
--添加外鍵約束
?ALTER TABLE PROJECTS
?????? ADD? ( CONSTRAINT FK_PROJECTSSITECODE
????????????? FOREIGN KEY (SITECODE)
???????????????????????????? REFERENCES SITES ) ;
?
1.1? 命名規范
1、? 所有jsp,action映射路徑均為小寫字母,如有需要可以使用小寫字母+數字:例如:
/projectlist,/projetlist.jsp
2、? 所有<html:form>中的元素(如文本框,列表框等)名稱都使用java規范的變量命名方式(變量由一個或多個單詞組成,第一個單詞小寫,第一個單詞后的單詞首字母大寫),例如:
<html:text styleClass="input" maxlength="10" property="projectCode" size="30"/>
3、? 其它方面均遵守SUN推薦的編碼規范。
1.2? 文件
1.2.1?????????? projectlist.jsp
該jsp頁面用來顯示項目信息列表,并提供查詢功能。同時,提供按鈕將用戶導向到添加、修改、刪除功能。
1.2.2?????????? projectform.jsp
在執行添加、修改操作之前,需要提供一個form供用戶輸入數據,在執行刪除操作之前,需要提供一個form將被刪除數據顯示出來,供用戶確認。該jsp頁面就是用來滿足這些需要,提供對單條項目信息的顯示,根據具體的操作類型(創建、修改、刪除),數據被顯示在可編輯的輸入控件中或不可編輯的label(文本標簽)上。
1.2.3?????????? success.jsp
添加、修改、刪除等操作正常完成后,提供一個頁面顯示"恭喜"信息,使得用戶能夠清楚知道自己的行為已經生效 。
1.2.4?????????? failed.jsp
添加、修改、刪除等操作異常失敗,提供一個頁面顯示"失敗"信息,使得用戶能夠清楚知道自己的行為已經失敗 。
1.2.5?????????? ProjectListSearchAction.java
"Project"+"List"+"Search"+"Action",組成了這個Action的名字,這是我個人的命名風格,表示這個Action會處理項目列表和查詢事務。在projectlist.jsp被裝載之前,ProjectListSearchAction需要將數據加載到scope指定的地方,供projectlist.jsp顯示,當用戶從projectlist.jsp中提交查詢請求,該Action需要處理查詢,并加載數據,供projectlist.jsp顯示。
1.2.6?????????? ProjectFormLoadAction
這個Action用來處理在顯示projectform.jsp之前,將所需要的數據加載到scope指定的范圍中,供projectform使用。
?
1.2.7?????????? ProjectFormSaveAction.java
這個Action用來處理用戶在projectform.jsp中提交的數據,根據用戶的操作類型,完成具體的操作,并將合適的提示頁面(success.jsp or failed.jsp)顯示給用戶。
1.2.8?????????? web.xml
在Struts安裝那一節,我們已經知道web.xml文件的作用,通過這個文件,我們可以將ActionServlet配置好,用以截獲用戶對Struts應用的訪問請求。下面是示例程序所用的web.xml內容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "<web-app>
? <display-name>simpledemo</display-name>
? <description>Demo for using STRUTS to do some thing about CRUD(Create,Read,Update,Delete) and any more....</description>
? <servlet>
??? <servlet-name>action</servlet-name>
??? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
??? <init-param>
????? <param-name>config</param-name>
????? <param-value>/WEB-INF/struts-config.xml</param-value>
??? </init-param>
??? <init-param>
????? <param-name>debug</param-name>
????? <param-value>2</param-value>
??? </init-param>
??? <load-on-startup>2</load-on-startup>
? </servlet>
? <servlet>
??? <servlet-name>debugjsp</servlet-name>
??? <description>Added to compile JSPs with debug info</description>
??? <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
??? <init-param>
????? <param-name>classdebuginfo</param-name>
????? <param-value>true</param-value>
??? </init-param>
??? <load-on-startup>3</load-on-startup>
? </servlet>
? <servlet-mapping>
??? <servlet-name>action</servlet-name>
??? <url-pattern>*.do</url-pattern>
? </servlet-mapping>
? <servlet-mapping>
??? <servlet-name>debugjsp</servlet-name>
??? <url-pattern>*.jsp</url-pattern>
? </servlet-mapping>
? <session-config>
??? <session-timeout>20</session-timeout>
? </session-config>
? <welcome-file-list>
??? <welcome-file>projectsearch.do</welcome-file>
? </welcome-file-list>
? <taglib>
??? <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
??? <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
? </taglib>
? <taglib>
??? <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
??? <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
? </taglib>
? <taglib>
??? <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
??? <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
? </taglib>
? <taglib>
??? <taglib-uri>/WEB-INF/struts-template.tld</taglib-uri>
??? <taglib-location>/WEB-INF/struts-template.tld</taglib-location>
? </taglib>
? <taglib>
??? <taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri>
??? <taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
? </taglib>
? <taglib>
??? <taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
??? <taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
? </taglib>
</web-app>
1.1.1?????????? 資源文件
使用資源文件來放置標簽顯示值,提示信息等,如果你的Struts應用有國際化的要求,那么資源文件一定要好好地利用,就算沒有國際化需求,使用資源文件也可以統一應用程序用語(例如統一的提示信息,標簽信息等),而且當用語發生變化(如"小時"變成"鐘頭" ),很容易統一進行修改。下面是示例程序所用到的資源文件內容(application.properties):
#System global labels
button_cancel = 取消
button_edit = 修改
button_delete = 刪除
button_save = 保存
button_submit = 確認
?
#lables for project
projectcontroller.title = 管理項目
project.project_create = 添加項目
project.project_modify = 修改項目
project.project_list = 已添加項目列表
project.projectcode = 項目代碼
project.projectname = 項目名稱
project.discount = 項目分成比例
project.site = 所屬小區
# Standard error messages for validator framework checks
errors.required={0} is required.
errors.minlength={0} cannot be less than {1} characters.
errors.maxlength={0} cannot be greater than {2} characters.
errors.invalid={0} is invalid.
errors.byte={0} must be an byte.
errors.short={0} must be an short.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.float={0} must be an float.
errors.double={0} must be an double.
errors.date={0} is not a date.
errors.range={0} is not in the range {1} through {2}.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
那么,如何在Struts應用中引用資源文件呢?
首先需要在Struts配置文件(Struts-config.xml)中指明配置文件的路徑,在配置文件中添加如下一行信息:
<message-resources parameter="ApplicationResources_CN" />
parameter所指的就是配置文件,注意,為什么這里指明的是applicationResources_CN,而不是上面提到的application.properties?這是為了能在Struts中正確顯示中文,利用jdk自帶的native2ascii程序對application.properties作了編碼轉換,編碼轉換后的文件名為ApplicationResources_CN.properties,擴展名可以省略。
需要注意的是,改配置文件一定要放在classpath范圍內,一般放置在WEB-INF/classes目錄下,如果放在classes的子目錄下,其指引方式和java包一樣,例如在WEB-INF/classes/com目錄下,則應該用如下語句指引:
<message-resources parameter="com.ApplicationResources_CN" />
小技巧
進行中文編碼轉換的命令如下:
native2ascii -encoding gb2312 application.properties ApplicationResources_CN.properties
在配置文件聲明了對資源文件的引用之后,就可以在Struts提供的標簽以及校驗框架等其它地方使用這些資源,具體使用方法請查看相關標簽和配置文件說明。
1.1.2?????????? struts-config.xml
該文件定義了Struts應用中的Action,ActionForm,插件,資源引用等信息,示例程序struts-config.xml文件內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "<struts-config>
? <form-beans>
????? <!--項目增、刪、改form定義 begin-->
??? <form-bean name="projectForm" type="com.ort.strutsdemo.simpledemo.ui.ProjectForm">
??? </form-bean>
??? <!--項目增、刪、改form定義 結束-->
??? <!--進入項目增、刪、改界面之前參數傳遞Form begin-->
??? <form-bean name="projectLoadForm" type="org.apache.struts.action.DynaActionForm">
????? <form-property name="action" size="10" type="java.lang.String" initial="create"/>
????? <form-property name="projectCode" size="30" type="java.lang.String" initial=""/>
??? </form-bean>
??? <!--進入項目增、刪、改界面之前參數傳遞Form end-->
??? <!--項目查詢Form定義 Begin-->
??? <form-bean name="projectSearchForm" type="org.apache.struts.action.DynaActionForm">
??????? <form-property name="projectCode" size="10" type="java.lang.String" initial=""/>
??????? <form-property name="projectCodeSign" size="10" type="java.lang.String" initial=""/>
??????? <form-property name="projectName" size="10" type="java.lang.String" initial=""/>
??????? <form-property name="projectNameSign" size="10" type="java.lang.String" initial=""/>
??????? <form-property name="page" size="10" type="java.lang.Integer" initial="1"/>
??????? <form-property name="pageCount" size="10" type="java.lang.Integer" initial="1"/>
??? </form-bean>
??? <!--項目查詢Form定義 End-->
? </form-beans>
? <global-forwards>
??? <forward name="projectlist" path="/projectlist.jsp" />
??? <forward name="projectform" path="/projectform.jsp" />
??? <forward name="failed" path="/failed.jsp" />
??? <forward name="success" path="/success.jsp" />
? </global-forwards>
?
? <action-mappings>
??? <action input="projectform" name="projectForm" path="/projectformsave" scope="request" type="com.ort.strutsdemo.simpledemo.controller.ProjectFormSaveAction" validate="true">
????? <forward name="success" path="/projectsearch.do" redirect="true" />
????? <forward name="success.return" path="/projectsearch.do" redirect="true" />
????? <forward name="cancel" path="/projectsearch.do" redirect="true" />
????? <forward name="failed" path="/failed.jsp" />
????? <forward name="failed.return" path="/projectsearch.do" />
??? </action>
??? <action input="projectlist" name="projectLoadForm" path="/projectformload" scope="request" type="com.ort.strutsdemo.simpledemo.controller.ProjectFormLoadAction" validate="false">
????? <forward name="success" path="/projectform.jsp" />
????? <forward name="failed" path="/failed.jsp" />
????? <forward name="failed.return" path="/projectsearch.do" redirect="true" />
??? </action>
???? <action input="projectlist" name="projectSearchForm" path="/projectsearch" scope="request" type="com.ort.strutsdemo.simpledemo.controller.ProjectListSearchAction" validate="false">
????? <forward name="success" path="/projectlist.jsp"/>
????? <forward name="failed" path="/failed.jsp" />
????? <forward name="failed.return" path="/projectsearch.do" redirect="true" />
??? </action>
? </action-mappings>
? <controller>
??? <set-property property="inputForward" value="true" />
? </controller>
? <message-resources parameter="ApplicationResources_CN" />
? <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
??? <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />
? </plug-in>
</struts-config>
1.1? CRUD
CRUD(Create,Retrieve or Read?,Update,Delete)是信息管理系統中經常要執行的操作的簡稱。接下來我們分別描述這幾種操作的執行流程。
1.1.1?????????? 查詢項目信息
執行項目信息查詢列表顯示的文件請求處理順序如下:
1、? ProjectListSearchAction
2、? projectlist.jsp
?
ProjectListSearchAction代碼如下:
package com.ort.strutsdemo.simpledemo.controller;
?
/**
?* <p>Title: Struts Training </p>
?* <p>Description: Struts內部培訓Demo</p>
?* <p>Copyright: Copyright (c) 2004</p>
?* <p>Company: </p>
?* @author Liuz
?* @version 1.0
?*/
import org.apache.struts.action.*;
import javax.servlet.http.*;
import com.ort.strutsdemo.simpledemo.business.BusinessDelegate;
import com.boss.module.operation.object.Project;
import com.ort.strutsdemo.simpledemo.Constants;
import com.ort.strutsdemo.simpledemo.controller.exception.ExceptionBean;
import com.boss.module.operation.object.searchresult.help.ResultSetIterator;
import com.ort.strutsdemo.simpledemo.ui.ProjectForm;
import com.boss.module.operation.object.searchgene.ProjectSearchGene;
?
/**
?*
?* <p>Title: Struts Training </p>
?* <p>Description: 項目查詢結果數據的裝載</p>
?* <p>Copyright: Copyright (c) 2004</p>
?* <p>Company: ORT</p>
?* @author Liuz
?* @version 1.0
?*/
public class ProjectListSearchAction
??? extends Action {
??? BusinessDelegate businessDelegate = null;
??? public ActionForward execute(ActionMapping actionMapping,
???????????????????????????????? ActionForm actionForm,
???????????????????????????????? HttpServletRequest request,
???????????????????????????????? HttpServletResponse response) {
??????? try {
??????????? DynaActionForm form = (DynaActionForm) actionForm;
??????????? //定義分頁所需要變量
??????????? int page= ((Integer)form.get("page")).intValue();
??????????? int pageSize = 5;
??????????? int allSize = 0;
??????????? int pageCount = 0;
??????????? //獲取用戶輸入查詢值,并形成查詢條件
??????????? String projectCode = (String)form.get("projectCode");
??????????? String projectCodeSign = (String)form.get("projectCodeSign");
??????????? String projectName = (String)form.get("projectName");
??????????? String projectNameSign = (String)form.get("projectNameSign");
??????????? ProjectSearchGene searchGene = new ProjectSearchGene(); //searchGene為一查詢精靈,用以處理查詢操作,在此不用過多關注,有機會在另文介紹
??????????? searchGene.setProjectCode(projectCode, projectCodeSign);
??????????? searchGene.setProjectName(projectName, projectNameSign);
?
?
??????????? //裝載當前頁面所需要顯示項目列表數據
???????????? BusinessDelegate businessDelegate = BusinessDelegate.getInstance(); //業務層操作,不用關注
???????????? ResultSetIterator projectIterator = businessDelegate.
???????????????? getProjectIterator(searchGene,pageSize);
???????????? java.util.List projects = projectIterator.getElements(page);
???????????? if (projects == null) {
???????????????? projects = new java.util.ArrayList();
???????????? }
???????????? //將項目列表查詢結果放置到請求對象中
??????????? request.removeAttribute(Constants.PROJECT_LISTFORM_KEY);
??????????? request.setAttribute(Constants.PROJECT_LISTFORM_KEY, projects);
??????????? //計算總頁數
??????????? allSize = projectIterator.getAllSize();
??????????? pageCount = (allSize % pageSize == 0) ? allSize / pageSize :
??????????????? allSize / pageSize + 1;
?????????? // System.err.print(pageCount);
??????????? form.set("pageCount", new Integer(pageCount));
?
??????????? //裝載當前頁面所需要現實小區信息
??????????? ResultSetIterator siteIterator = businessDelegate.getSitesIterator();
??????????? java.util.List sites = siteIterator.getElements(1);
??????????? request.getSession().setAttribute(Constants.SITE_LISTFORM_KEY,
????????????????????????????????????????????? sites);
??????????? //重定向到mapping中配置的input頁面
//??????????? return actionMapping.findForward("success");
??????????? return actionMapping.getInputForward();
??????? }
??????? catch (Exception ex) {
??????????? com.ipbs.util.Log.println(
??????????????? "[ProjectListSearchAction.java][Exception]:" + ex.getMessage());
??????????? ExceptionBean exception = new ExceptionBean();
??????????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
??????????? exception.setReturnPath(actionMapping.findForward("failed.return").
??????????????????????????????????? getPath());
??????????? request.setAttribute(Constants.EXCEPTION_BEAN, exception);
??????????? return actionMapping.findForward("failed");
??????? }
??? }
}
在上面的代碼中,應用了一個常量類(Constants),為了讓大家看得更加明白,將其代碼顯示如下:
package com.ort.strutsdemo.simpledemo;
?
/**
?* <p>Title: Struts Training </p>
?* <p>Description: Struts內部培訓Demo</p>
?* <p>Copyright: Copyright (c) 2004</p>
?* <p>Company: </p>
?* @author Liuz
?* @version 1.0
?*/
?
public class Constants {
? public static final String PROJECT_SINGLEFORM_KEY = "projectForm";
? public static final String PROJECT_LISTFORM_KEY = "PROJECTS";
? public static final String PROJECT_SEARCHFORM_KEY = "SEARCHPROJECTS";
? public static final String PROJECT_CONTROLLERFORM_KEY = "PROJECTCONTROLLER";
? public static final String SITE_LISTFORM_KEY = "SITES";
? public static final String SITE_SINGLEFORM_KEY = "SITE";
?
? public static final String EXCEPTION_BEAN = "EXCEPTIONBEAN";
? /**
?? * 通過識別異常的基礎類型,返回易懂的提示信息
?? * @param ex Exception 異常
?? * @return String 異常的描述信息
?? */
? public static final String getExceptionMsg(Exception ex){
????? String returnMsg="";
????? if(ex!=null){
????????? if (ex instanceof com.boss.module.operation.command.exception.AlreadyExistException){
????????????? returnMsg = "數據已存在!";
????????????? return returnMsg;
????????? }else if (ex instanceof com.boss.module.operation.command.exception.DbException){
????????????? returnMsg = "數據庫錯誤!";
????????????? return returnMsg;
????????? }else if(ex instanceof com.boss.module.operation.command.exception.InvalidObjectException){
????????????? returnMsg = "無效的數據!";
????????????? return returnMsg;
????????? }else if(ex instanceof com.boss.module.operation.command.exception.InvalidPkException){
????????????? returnMsg = "無效的主鍵!";
????????????? return returnMsg;
????????? }else if(ex instanceof com.boss.module.operation.command.exception.NotFoundException){
????????????? returnMsg = "數據不存在!";
????????????? return returnMsg;
????????? }else if(ex instanceof com.boss.module.operation.command.exception.UnAuthorizationException){
????????????? returnMsg = "無權限!";
????????????? return returnMsg;
????????? }else if(ex instanceof com.boss.module.operation.object.searchresult.exception.IteratorException){
????????????? returnMsg = "獲取列表數據異常!";
????????????? return returnMsg;
????????? }
????? }
??? return returnMsg;
? }
}
?
projectlist.jsp內容如下:
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="/WEB-INF/struts-nested.tld" prefix="nested" %>
<%@ taglib uri="/WEB-INF/struts-template.tld" prefix="template" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ page contentType="text/html; charset=GB2312" %>
?
<html:html>
<head>
<!-projectlist.title為資源引用-->
<title><bean:message key="projectlist.title"/></title>
<link href="css/main.css" rel="stylesheet" type="text/css">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312"></head>
<body bgcolor="#ffffff">
<html:form action="projectsearch">
??? <table width="70%"? border="0" align="center" cellpadding="3" cellspacing="1" class="tablebodycolor">
??? <tr class="tdbodycolor">
????? <td colspan="2" class="tdheadcolor">
????????????? <bean:message key="project.project_search"/>
????? </td>
??? </tr>
??? <tr class="tdbodycolor">
????? <td width="15%"><div align="right"><bean:message key="project.projectcode"/></div></td>
????? <td width="85%">
????????? <html:select property="projectCodeSign">
????????????? <html:option value="like">like</html:option>
????????????? <html:option value="not like">not like</html:option>
????????????? <html:option value="=">equal</html:option>
????????????? <html:option value="<>">not equal</html:option>
????????? </html:select>
?<html:text styleClass="input" maxlength="30" property="projectCode" size="30"/>
????? </td>
??? </tr>
??? <tr class="tdbodycolor">
????? <td><div align="right"><bean:message key="project.projectname"/></div></td>
????? <td>
????????? <html:select property="projectNameSign">
????????????? <html:option value="like">like</html:option>
????????????? <html:option value="not like">not like</html:option>
????????????? <html:option value="=">equal</html:option>
????????????? <html:option value="<>">not equal</html:option>
????????? </html:select>
????????? <html:text styleClass="input" maxlength="30" property="projectName" size="30"/>
????? </td>
??? </tr>
??? <tr class="tdbodycolor">
?
?? <tr class="tdbodycolor">
????? <td colspan="2"><div align="center">
????????? <html:hidden property="page"/>
????????? <html:hidden property="pageCount"/>
??????? <html:submit><bean:message key="button_search"/></html:submit>
????? </div></td>
??? </tr>
??? </tr>
? </table>
</html:form>
<table width="70%" border="0" align="center" cellpadding="2" cellspacing="1" class="tablebodycolor">
? <tr class="tdheadcolor">
??? <td height="19" colspan="6" class="tdbodycolor">
?????? <input type="button" name="button_create"
??????????? value="<bean:message key="button_create"/>" onclick="javascript:window.location='projectformload.do?action=create'"/>
??? </td>
? </tr>
</table>
<table width="70%" border="0" align="center" cellpadding="2" cellspacing="1" class="tablebodycolor">
? <tr class="tdheadcolor">
??? <td height="19" colspan="6" class="tdbodycolor"><bean:message key="project.project_list"/></td>
? </tr>
? <tr class="tdheadcolor">
??? <td width="92"><div align="center"><bean:message key="project.projectcode"/></div></td>
??? <td width="182"><div align="center"><bean:message key="project.projectname"/></div></td>
??? <td width="70"><div align="center"><bean:message key="project.discount"/></div></td>
??? <td width="139"><div align="center"><bean:message key="project.site"/></div></td>
??? <td width="83"><div align="center"><bean:message key="button_edit" /></div></td>
??? <td width="83"><div align="center"><bean:message key="button_delete"/></div></td>
? </tr>
? <logic:iterate id="project" name="PROJECTS">
? <tr class="tdbodycolor">
??? <td><div align="center"><bean:write name="project" property="projectCode"/></div></td>
??? <td><div align="center"><bean:write name="project" property="projectName"/></div></td>
??? <td><div align="center"><bean:write name="project" property="discount"/></div></td>
??? <td><div align="center"><bean:define id="site" name="project" property="site">
?????????????????????????? </bean:define>
?????????????????????????? <bean:write name="site" property="siteName"/></div></td>
??? <td><div align="center">
??????? <input type="button" name="button_edit"
??????????? value="<bean:message key="button_edit"/>" onclick="javascript:window.location='projectformload.do?action=modify&projectCode=<bean:write name="project" property="projectCode"/>'"/>
??? </div></td>
??? <td><div align="center">
?????? <input type="button" name="button_delete"
??????????? value="<bean:message key="button_delete"/>" onclick="javascript:window.location='projectformload.do?action=delete&projectCode=<bean:write name="project" property="projectCode"/>'"/>
??? </div></td>
? </tr>
? </logic:iterate>
</table>
?
<!------分頁----->
<script language="javascript">
?function firstPage(){
??????????? document.projectSearchForm.page.value = 1;
??????????? document.projectSearchForm.submit();
?}
??????? function previousPage(){
??????????? document.projectSearchForm.page.value = parseInt(document.projectSearchForm.page.value)-1;
??????????? document.projectSearchForm.submit();
??????? }
??????? function nextPage(){
??????????? document.projectSearchForm.page.value = parseInt(document.projectSearchForm.page.value)+1;
??????????? document.projectSearchForm.submit();
??????? }
??????? function lastPage(){
??????????? document.projectSearchForm.page.value = document.projectSearchForm.pageCount.value;
??????????? document.projectSearchForm.submit();
??????? }
</script>
<table width=600 cellspacing="0" cellpadding="0" align="center">
??????? <tr>
????????? <td colspan=2><br>
????????????? <div align="center">
??<bean:define id="currPageOb" property="page" name="projectSearchForm"/>
??????????????? <bean:define id="pageCountOb" property="pageCount" name="projectSearchForm"/>
??????????????? <%int currPage = ((Integer)(currPageOb)).intValue();
??????????????? int pageCount = ((Integer)(pageCountOb)).intValue();%>
??????????????? <%if(currPage>1){%>
?????? ?????????? <a href="javascript:firstPage();">首頁</a>
????????? ?? <a href="javascript:previousPage();">上一頁</a>
??????????????? <%}%>
??????????????? <%if(pageCount>currPage){%>
????????? ?? <a href="javascript:nextPage();">下一頁</a>
???????????????????? ?<a href="javascript:lastPage();">末頁</a>
??????????????? <%}%>
??????????????? 當前頁:<%=currPage%>,
??????????????? 共<%=pageCount%>頁
????????????? </div>
???????????? </td>
??????? </tr>
</table>
<!------分頁----->
?
</body>
</html:html>
界面顯示如下:
?
1.1.1?????????? 創建項目信息
執行項目信息查詢列表顯示的文件請求處理順序如下:
1、? ProjectListSearchAction
2、? projectlist.jsp
3、? ProjectFormLoadAction
4、? projectform.jsp
5、? ProjectFormSaveAction
6、? success.jsp or failed.jsp
7、? ProjectListSearchAction
?
ProjectFormLoadAction內容如下:
package com.ort.strutsdemo.simpledemo.controller;
?
?
/**
?* <p>Title: Struts Training </p>
?* <p>Description: Struts內部培訓Demo</p>
?* <p>Copyright: Copyright (c) 2004</p>
?* <p>Company: </p>
?* @author Liuz
?* @version 1.0
?*/
import org.apache.struts.action.*;
import javax.servlet.http.*;
import com.ort.strutsdemo.simpledemo.business.BusinessDelegate;
import com.boss.module.operation.object.Project;
import com.ort.strutsdemo.simpledemo.Constants;
import com.ort.strutsdemo.simpledemo.controller.exception.ExceptionBean;
import com.ort.strutsdemo.simpledemo.ui.ProjectForm;
import com.boss.module.operation.object.searchresult.help.ResultSetIterator;
?
public class ProjectFormLoadAction
??? extends Action {
? BusinessDelegate businessDelegate = null;
? public ActionForward execute(ActionMapping actionMapping,
?????????????????????????????? ActionForm actionForm,
?????????????????????????????? HttpServletRequest request,
?????????????????????????????? HttpServletResponse response) {
??? businessDelegate = BusinessDelegate.getInstance();
??? DynaActionForm form = (DynaActionForm)actionForm;
??? String action = (String)form.get("action");
??? String projectCode = (String)form.get("projectCode");
??? try {
??????? Project project = null;
??????? if ( ("create".equals(action))) {
??????????? project = new Project();
??????? }
??????? else {
??????????? project = businessDelegate.getProject(projectCode);
??????? }
??????? project.setAction(action);
??????? ProjectForm modifyForm = new ProjectForm();
??????? org.apache.commons.beanutils.PropertyUtils.copyProperties(
??????????? modifyForm, project);
??????? if (! ("create".equals(action))) {
??????????? modifyForm.setSiteCode(project.getSite().getSiteCode());
??????? }
??????? request.setAttribute(Constants.PROJECT_SINGLEFORM_KEY, modifyForm); //為下一個頁面,即projectform.jsp的顯示提供數據
??????? //裝載當前頁面所需要顯示小區信息
??????? ResultSetIterator siteIterator = businessDelegate.getSitesIterator();
??????? java.util.List sites = siteIterator.getElements(1);
??????? request.getSession().setAttribute(Constants.SITE_LISTFORM_KEY,
????????????????????????????????????????? sites);
?
??????? return actionMapping.findForward("success");
??? }
??? catch (Exception ex) {
??????? com.ipbs.util.Log.println("[ProjectFormLoadAction.java][Exception]:"+ex.getMessage());
??????? ExceptionBean exception = new ExceptionBean();
??????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
??????? exception.setReturnPath(actionMapping.findForward("failed.return").getPath());
??????? request.setAttribute(Constants.EXCEPTION_BEAN,exception);
??????? return actionMapping.findForward("failed");
??? }
? }
}
?
projectform.jsp內容如下:
<%
/**
?* 文件名:projectform.jsp
?* 描述:在執行添加、修改操作之前,需要提供一個form供用戶輸入數據,在執行刪除操作之前,
?*???? 需要提供一個form將被刪除數據顯示出來,供用戶確認。該jsp頁面就是用來滿足這些需要,
?*???? 提供對單條項目信息的顯示,根據具體的操作類型(創建、修改、刪除),數據被顯示在可編
?*???? 輯的輸入控件中或不可編輯的
?*
?*+------------------------------------------------------------------------------
?* 更改歷史
?* 更改時間?????????????????? 更改人??????? 目標版本???????????? 更改內容
?*+------------------------------------------------------------------------------
?* 2004-04-21 16:09????????? liuz???????? 1.00.000????????????? 創建
?*
?*
?*/
%>
<%@ page contentType="text/html; charset=GB2312" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head>
<title>
<logic:equal value="modify" property="action"? name="projectForm">
??? <bean:message key="project.project_modify"/>
</logic:equal>
<logic:equal value="create" property="action" name="projectForm">
? <bean:message key="project.project_create"/>
</logic:equal>
<logic:equal value="delete" property="action" name="projectForm">
? <bean:message key="project.project_delete"/>
</logic:equal>
</title>
<link href="css/main.css" rel="stylesheet" type="text/css">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body bgcolor="#ffffff">
?
<html:form action="/projectformsave" method="post" onsubmit="return validateProjectForm(this);">
? <table width="70%"? border="0" align="center" cellpadding="3" cellspacing="1" class="tablebodycolor">
??? <tr class="tdbodycolor">
????? <td colspan="2" class="tdheadcolor">
????????? <logic:equal value="modify" property="action" name="projectForm">
????? ??<bean:message key="project.project_modify"/>
????????? </logic:equal>
????????? <logic:equal value="create" property="action" name="projectForm">
????????????? <bean:message key="project.project_create"/>
????????? </logic:equal>
????????? <logic:equal value="delete" property="action" name="projectForm">
????????????? <bean:message key="project.project_delete"/>
????????? </logic:equal>
????? </td>
??? </tr>
??? <tr class="tdbodycolor">
????? <td width="15%"><div align="right"><bean:message key="project.projectcode"/></div></td>
????? <td width="85%">
????????? <logic:equal value="modify" property="action" name="projectForm">
????? ??<html:hidden property="projectCode" write="true"/>
????????? </logic:equal>
????????? <logic:notEqual value="modify" property="action" name="projectForm">
????????????? <html:text styleClass="input" maxlength="10" property="projectCode" size="30"/>
????????? </logic:notEqual>
????????? </td>
??? </tr>
??? <tr class="tdbodycolor">
????? <td><div align="right"><bean:message key="project.projectname"/></div></td>
????? <td><html:text styleClass="input" maxlength="30" property="projectName" size="30"/></td>
??? </tr>
??? <tr class="tdbodycolor">
????? <td><div align="right"><bean:message key="project.discount"/></div></td>
????? <td><html:text styleClass="input" maxlength="10" property="discount" size="10"/>
????? %</td>
??? </tr>
??? <tr class="tdbodycolor">
????? <td><div align="right"><bean:message key="project.site"/></div></td>
????? <td>
????????? <html:select property="siteCode">
????????????? <html:options? collection="SITES"? labelProperty="siteName"? property="siteCode"/>
????????? </html:select>
????? </td>
??? </tr>
??? <tr class="tdbodycolor">
????? <td colspan="2"><div align="center">
????????? <html:hidden property="action" />
??????? <html:submit><bean:message key="button_submit"/></html:submit>
???????????? <html:cancel><bean:message key="button_cancel"/>
???????????? </html:cancel>
????? </div></td>
??? </tr>
? </table>
</html:form>
<html:javascript formName="projectForm"
??????? dynamicJavascript="true"
???????? staticJavascript="false"/>
<script language="Javascript1.1" src="staticJavascript.jsp"></script>
</body>
</html>
界面顯示效果如下:
?圖6.3.2.1 修改項目圖
?圖6.3.2.2 添加項目圖
?圖6.3.2.3 刪除確認圖
最后,信息新增、修改或刪除確認后,需要提交給ProjectFormSaveAction進行,ProjectFormSaveAction內容如下:
package com.ort.strutsdemo.simpledemo.controller;
?
import org.apache.struts.action.*;
import javax.servlet.http.*;
import com.ort.strutsdemo.simpledemo.business.BusinessDelegate;
import com.boss.module.operation.object.Project;
import com.boss.module.operation.object.Site;
import com.ort.strutsdemo.simpledemo.Constants;
import com.ort.strutsdemo.simpledemo.ui.ProjectForm;
import com.ort.strutsdemo.simpledemo.controller.exception.ExceptionBean;
import com.ipbs.util.web.ParamUtils;
/**
?*
?* <p>Title: Struts Training </p>
?* <p>Description: 項目管理功能頁面的導向,以及頁面所需要數據的初始化,同時,處理刪除操作</p>
?* <p>Copyright: Copyright (c) 2004</p>
?* <p>Company: </p>
?* @author Liuz
?* @version 1.0
?*/
public class ProjectFormSaveAction
??? extends Action {
? BusinessDelegate businessDelegate = null;
? public ActionForward execute(ActionMapping actionMapping,
?????????????????????????????? ActionForm actionForm,
?????????????????????????????? HttpServletRequest request,
?????????????????????????????? HttpServletResponse response) {
??? businessDelegate = BusinessDelegate.getInstance();
??? ProjectForm form = (ProjectForm)actionForm;
??? ActionForward forward = null;
??? String action = form.getAction();
??? if (this.isCancelled(request)) {
??????? return (actionMapping.findForward("cancel"));
??? }
??? if("modify".equals(action)){
??????? forward = modifyProject(actionMapping, actionForm,
?????????????????????????????????????????? request, response);
??? }
??? else if ("create".equals(action)) {
????? forward = createProject(actionMapping, actionForm,request, response);
??? }else if("delete".equals(action)){
?????? forward = deleteProject(actionMapping, actionForm,request, response);
??? }
??? return forward;
? }
?
?
? public ActionForward modifyProject(ActionMapping actionMapping,
???????????????????????????????????? ActionForm actionForm,
??????????????????????????????????? HttpServletRequest request,
??????????????????????????????????? HttpServletResponse response
??????????????????????????????????? )
{
???? ProjectForm form = (ProjectForm) actionForm;
???? String projectCode = form.getProjectCode();
???? String projectName = form.getProjectName();
???? String siteCode = form.getSiteCode();
???? double discount = form.getDiscount();
????? try{
????????? Site site = businessDelegate.getSite(siteCode);
????????? Project project = new Project();
????????? project.setProjectCode(projectCode);
????????? project.setProjectName(projectName);
????????? project.setDiscount(discount);
????????? project.setSite(site);
????????? businessDelegate.modifyProject(project);
????????? return actionMapping.findForward("success");
????? }catch(Exception ex){
????????? com.ipbs.util.Log.println(
????????????? "[ProjectFormSaveAction.java][Exception]:" + ex.getMessage());
????????? ExceptionBean exception = new ExceptionBean();
????????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
????????? exception.setReturnPath(actionMapping.getPath());
????????? request.setAttribute(Constants.EXCEPTION_BEAN, exception);
????????? return actionMapping.findForward("failed");
????? }
? }
?
? public ActionForward createProject(ActionMapping actionMapping,
???????????????????????????????????? ActionForm actionForm,
??????????????????????????????????? HttpServletRequest request,
??????????????????????????????????? HttpServletResponse response
??????????????????????????????????? ) {
???? ProjectForm form = (ProjectForm) actionForm;
???? String projectCode = form.getProjectCode();
???? String projectName = form.getProjectName();
???? String siteCode = form.getSiteCode();
???? double discount = form.getDiscount();
???? try{
???????? Site site = businessDelegate.getSite(siteCode);
???????? Project project = new Project();
???????? project.setProjectCode(projectCode);
???????? project.setProjectName(projectName);
???????? project.setDiscount(discount);
???????? project.setSite(site);
???????? businessDelegate.createProject(project);
???????? return actionMapping.findForward("success");
???? }catch(Exception ex){
???????? com.ipbs.util.Log.println(
????????????? "[ProjectFormSaveAction.java][Exception]:" + ex.getMessage());
????????? ExceptionBean exception = new ExceptionBean();
????????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
????????? exception.setReturnPath(actionMapping.getPath());
????????? request.setAttribute(Constants.EXCEPTION_BEAN, exception);
????????? return actionMapping.findForward("failed");
???? }
? }
?
? public ActionForward deleteProject(ActionMapping actionMapping,
???????????????????????????????????? ActionForm actionForm,
??????????????????????????????????? HttpServletRequest request,
??????????????????????????????????? HttpServletResponse response
??????????????????????????????????? ) {
???? ProjectForm form = (ProjectForm)actionForm;
???? String projectCode = form.getProjectCode();
???? try{
???????? businessDelegate.deleteProject(projectCode);
???????? request.removeAttribute(Constants.PROJECT_SINGLEFORM_KEY);
???????? return actionMapping.findForward("success");
???? }catch(Exception ex){
???????? com.ipbs.util.Log.println(
???????????? "[ProjectControllerAction.java][Exception]:" + ex.getMessage());
???????? ExceptionBean exception = new ExceptionBean();
???????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
???????? exception.setReturnPath(actionMapping.findForward("failed.return").getPath());
???????? request.setAttribute(Constants.EXCEPTION_BEAN,exception);
???????? return actionMapping.findForward("failed");
?
???? }
? }
?
}
至此為止,該示例項目所使用的絕大部分內容已經介紹完了,因為時間問題,沒有對示例程序進行很詳細的一一講解,代碼上有些簡單的注釋,希望你能夠看明白,如果大家對以上代碼有疑問會發現什么問題,可以發郵件和我溝通(lzasp800@sina.com),另外,我保證上面的代碼是真實可運行的,如果用心看,也是可以看明白的(當然只有文章中所列的代碼是無法通過編譯的)。
個人覺得,做Struts項目非常重要的一個環節就是請求處理流程設計,這也是本示例強調的重點,弄清楚了這一點,看代碼也會容易很多。
1.1.1?????????? 修改項目信息
請求處理流程如下,具體文件內容請查看前述內容:
1、? ProjectListSearchAction
2、? projectlist.jsp
3、? ProjectFormLoadAction
4、? projectform.jsp
5、? ProjectFormSaveAction
6、? success.jsp or failed.jsp
7、? ProjectListSearchAction
?
1.1.2?????????? 刪除項目信息
請求處理流程如下,具體文件內容請查看前述內容:
1、? ProjectListSearchAction
2、? projectlist.jsp
3、? ProjectFormLoadAction
4、? projectform.jsp
5、? ProjectFormSaveAction
6、? success.jsp or failed.jsp
7、? ProjectListSearchAction
?
?