引言
作為 Java Web 應用的典型框架,Struts 一直受到 Java 開發者的青睐,Struts 2 作為 Struts 發展的又一個裡程碑,以 WebWork 為基礎,提供了更易於使用,功能更強的 MVC 框架。同時它可以幫助開發人員更快速、高效、方便地實現一個 Java Web 應用系統。
對於開發人員來講,不同的項目可能需要不同的開發和運行 Struts 2 的平台,Struts 2 需要以下環境:Servlet API 2.4, JSP API 2.0, Java 5。IBM WebSphere Application Server 6.1(以下簡稱 WAS 6.1) 符合 Sturts 2 對運行環境的所有要求。另外,IBM Rational Software Architect 7(以下簡稱 RSA)提供了設計、開發各種應用的工具,其中包括創建和開發 Web 應用。二者的組合將是開發 Struts 2 的理想平台。
基於 RSA 及 WAS 搭建 Struts 2 開發平台
首先我們使用 RSA 創建一個動態 Web 項目,我們假設 RSA 中已經創建了一個 WAS 6.1 的服務器:
圖 1. 在 RSA 中創建 WAS 6.1 運行環境
在創建該項目的過程中,需要注意正確設置“目標運行時服務器”以及“動態 Web 模塊 版本“, 如下圖:
圖 2. 設置動態 Web 項目
其他按默認配置,點擊”完成”。創建完項目後,項目的基本包結構會自動生成,為了使該 Web 項目使用 Struts 2 框架,我們需要做一下配置:
配置 Struts 2 類庫
本示例使用 Struts 2.1.6 版本 , 所使用的類庫可以從 http://apache.etoak.com/struts/library/struts-2.1.6-lib.zip下載,如下圖,拷貝相應 jar 包到 Sample/WebContent/WEB-INF/lib 下。注意,本示例只拷貝了一些基本 jar 包,對於需要使用到 Struts 2 復雜功能的應用,需要另外拷貝相應 jar 包。
圖 3. 設置 Struts 2 依賴的 Jar 包
配置 web.xml
為了讓所有 web 請求通過 struts 2 框架處理,我們需要在 web.xml 中設置相應的 filter 以及 filter mapping, 對於 struts 2.1.6, 應設置成如下圖高亮部分:
圖 4. 在 web.xml 中設置 filter 和 filter mapping
對於 Struts 2 早期版本(2.1.3 之前),一般設置 filter 為 FilterDispatcher,具體如下:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Stuts 2 的 taglib 已經自動包含在 struts-core.jar, 服務器會自動找到相應 taglib,所以 web.xml 中不再需要設置 taglib。
配置 Web 容器
當在 WAS 6.1 上運行 struts 2 應用程序時,需要另外設置 web 容器的一個定制屬性:
com.ibm.ws.webcontainer.invokefilterscompatibility=true, 否則,所有 Struts 2 Action 不起作用。該設置可以在 WAS 控制台上設置如下:
圖 5. 設置 Web 容器
以上步驟完成了開發 struts 2 應用的准備工作,下面我們開發一個示例。
示例開發
該示例包括兩個頁面,第一個是管理員浏覽所有系統用戶(userList.jsp),第二個是管理員創建一個新用戶(user.jsp)。圖 6 和圖 7 分別是兩個示例的應用截圖。
圖 6 . 浏覽系統用戶
圖 7. 點擊 “創建”按鈕創建新用戶
點擊“提交”,如果通過校驗,則返回到第一個頁面,如果校驗失敗,仍回到當前頁面,並提示錯誤信息。
下面我們開發該應用的各個組件:
創建 Action
創建基類
通過基類實現 SessionAware, ServletRequestAware, ServletResponseAware 接口,可以使用 IOC 方式初始化 session, request 和 response。這種實現可以方便所有子類訪問 session, request 和 response。
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;
public class BaseAction extends ActionSupport
implements SessionAware, ServletRequestAware, ServletResponseAware{
private Map sessionMap;
private HttpServletRequest request;
private HttpServletResponse response;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
public void setSession(Map map){
this.sessionMap=map;
}
public HttpServletRequest getHttpServletRequest(){
return request;
}
public HttpServletResponse getHttpServletResponse(){
return response;
}
public Map getSession(){
return sessionMap;
}
}
創建 , 配置 Action
在該示例中,所有 form 表單提交使用一個 Action 類,該類的不同方法將作為不同表單提交的 Action。在該示例中,execute(), createOrEditUser(), submitUser(), deleteUser() 對應於不同表單對應的 action。
圖 8. UserAction 代碼
在 Struts 2 應用中,action 的配置文件應該位於類根路徑下,一個 Struts 2 應用中可以存在多個 action 配置文件,但這些文件需要在 struts.xml 中引用,本例所有 action 在 struts-user.xml 中定義,struts.xml 引用 struts-user.xml。
在 struts-user.xml 中可以看到不同的類方法被配置成不同的 Action, 如果沒有指定方法,那麼 struts 2 框架會默認調用 execute(), 例如:”showUserList”
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="user" extends="struts-default" namespace="/admin">
……………
<!-- user list -->
<action name="showUserList" class="sample.action.UserAction">
<result>/WEB-INF/page/userList.jsp</result>
</action>
<!-- add user -->
<action name="createOrEditUser"
class="sample.action.UserAction"
method="createOrEditUser">
<result>/WEB-INF/page/user.jsp</result>
</action>
<action name="submitUser"
class="sample.action.UserAction"
method="submitUser">
<result name="input">/WEB-INF/page/user.jsp</result>
<result name="userList" type="chain">showUserList</result>
</action>
……………
</package>
</struts>
在 Struts 2 中,如果一個 Action 需要調用另一個 Action, 可以設置 result 的 type 為 chain, 如前面代碼片段中的 <result name="userList"type="chain">showUserList</result>
開發頁面
在開發 Struts 2 應用時,不可避免會使用大量的 Struts 2 標簽,默認 Struts 2 解釋這些標簽生成 HTML 代碼時會加上一些額外的代碼,例如可能把 <tr><td> 自動加上,如果我們選擇自己靈活控制代碼格式,可以參考本示例在 struts.properties 裡設置 struts.ui.theme=simple, struts.properties 同樣位於類根路徑下:
圖 9.Struts 標簽及配置文件
下面我們通過 user.jsp 來展示 Struts 2 框架是如何關聯 Jsp, Action 以及表單字段的
<%@ taglib prefix="s2" uri="/struts-tags"%>
........
<s2:form action="submitUser" name="submitUser" namespace="/admin">
<table>
<tr>
<td><s2:text name="User.Name"/></td>
<td><s2:textfield name="user.userName"/></td>
</tr>
<tr>
<td><s2:text name="User.Password"/></td>
<td><s2:password name="user.password"/></td>
</tr>
<tr>
<td><s2:text name="User.Email"/></td>
<td><s2:textfield name="user.email"/></td>
</tr>
……………
</table>
</s2:form>
S2:form 標簽的 action 屬性指定了 form 要提交到的 action, 該 action 名已經在 struts-user.xml 中定義。Namespace 屬性和 struts-user.xml 中保持了一致,根據 struts-user.xml 的配置信息,該 form 就會被提交到 UserAction 的 submitUser 方法,但是表單各個字段值是如何被 Action 識別的呢?這是由代表 HTML 各種控件的 Struts 2 標簽 name 屬性指定的,例如 <s2:textfield name="user.userName"/> 就將該字段的值直接映射到 UserAction 的 user 實例變量的 userName 屬性,注意,這裡要求 user 對象對應的 User 類必須有一個空的構造函數,另外這裡的 user 應該是一個標准的 JavaBean, 所有屬性應該有 get,set 方法。
我們看一下表單提交後,action 是如何處理的:
public String submitUser() {
userService.addUser(user);
return "userList";
}
在調用該方法前,Struts 2 框架已經將所有表單參數自動組裝到 user 對象中,所以該方法可以直接訪問 user 對象,並對其做相應處理 , 該示例中的 userService 是負責 user 對象的增,刪,改操作。
添加完 user 對象,該 action 返回了一個字符串”userList”, 該字符串是 struts-user.xml 中 submitUser action 設置的一個返回結果,該返回結果其實是調用另一個 action 來顯示現有 user 列表。
表單校驗
Struts 2 提供了很多種校驗方式,一種是配置方式,可以用來校驗基本數據信息,另一種是編程校驗,可以用來校驗較為復雜的業務邏輯。
如果是通過配置來校驗,需要在 Action 類的同目錄下建一個 xml 文件,命名規則是 action 類名 -action 別名 -validation.xml。例如,本示例中:UserAction-submitUser-validation.xml, 當 submitUser action 被調用的時候,會先通過該 xml 配置進行校驗。下面代碼片段是本示例中配置的校驗參數,它指定了對哪些表單字段做哪些校驗:
<validators>
<field name="user.userName">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="User.UserName.Required"/>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">1</param>
<param name="maxLength">20</param>
<message key="User.UserName.Length"></message>
</field-validator>
</field>
<field name="user.email">
<field-validator type="requiredstring">
<message key="User.Email.Required"></message>
</field-validator>
<field-validator type="email">
<message key="User.Email.Format"></message>
</field-validator>
</field>
</validators>
Struts 2 定義了一些默認類型,例如 ,email, 可以校驗 email 格式,在上例中 user.email 指定了校驗表單的 email 字段,type=”requiredstring”表明該字段是必須的,type=”email”表明要檢驗 email 格式。所有的錯誤信息可以通過 key 來國際化。
另一種是通過編程校驗,在 UserAction 中有一個方法:
public void validateSubmitUser(){
if (user.getPassword().length() < 6){
this.addFieldError("user.password", getText("User.Password.Length"));
}
}
注意該方法的命名,validate+Action 方法名,也就是說該方法會在調用 submitUser Action 時自動被 Struts 2 框架調用。
國際化
Struts 2 中提供了各種機制對日期格式,數字格式做相應的國際化。例如通過 Action 同目錄下的 package.properties, 具體可以參照附件,示例代碼。
對於普通文本的國際化,需要做以下步驟:
設置國際化加載文件路徑
struts.properties 中的 struts.custom.i18n.resources 設置了 struts 2 需要自動加載的國際化文件
訪問國際化資源
任何繼承自 ActionSupport 的 Action 類都可以直接使用 getText 方法根據當前 locale 獲取相應鍵的值
在 JSP 中可以使用 OGNL 表達式 %{getText('key')} 或者 <s2:text name="key"/> 來訪問國際化資源。
最後,如果以上方式都不適用,可以使用 com.opensymphony.xwork2.util.LocalizedTextUtil 工具類來訪問國際化資源。
3.5 異常處理
一般來說,web 應用都會設置一些統一錯誤頁面,用來顯示那些未被 try catch 捕捉到的異常信息,該示例在 struts-user.xml 中加入了這樣的配置信息:
<global-results>
<result name="Exception">/WEB-INF/page/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="Exception"/>
</global-exception-mappings>
該配置表示在當前 package 下的所有 action, 一旦有 java.lang.Exception 拋出,並且沒有被相應 action 捕獲的話,系統將顯示錯誤頁面,error.jsp
另外,struts 2 也支持在某個 action 內部定義異常處理,例如:
<action name=”xxx” class=”xxx” method=”xxx”>
…….
<exception-mapping exception="java.lang.Exception" result="custom_error"/>
<result name="custom_error">/WEB-INF/page/common/error.jsp</result>
…….
</action>
在錯誤頁面中可以通過 <s2:property value="%{exception.message}" /> 顯示異常信息。
總結
通過該示例開發,我們對 Struts 2 開發平台以及 Struts 2 框架有了一個基本認識,不過要熟練應用 Struts 2,還需要了解它的各類標簽,OGNL 表達式。另外 Struts 2 還提供了一些高級特性,如 Interceptor 機制,IOC 機制。充分利用這些新特性會幫助我們更快,更方便地實現一些 Web 應用。
本文配套源碼