struts封裝表單數據

在Struts2.0裏面有一個非常牛*的功能就是支持更高級的POJO訪問

登陸頁面login.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W 3C //DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<s:form action="/test/loginAction.action" method="post">
    <s:textfield name="user.userName" label="用戶名"/>
    <s:password name="user.password" label="密碼"/>
    <s:submit/>
</s:form>
</body>
</html>

新增實體類User.java:
package tutorial;
public class User {
    private String userName;
    private String password;
     public String getPassword() {
       return password;
    }
    public void setPassword(String password) {
       this.password = password;
    }
    public String getUserName() {
       return userName;
    }

    public void setUserName(String userName) {
       this.userName = userName;
    }
}

請求處理LoginAction.java:
package tutorial;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
    private String msg="顯示默認消息";
    private User user;
    public User getUser() {
       return user;
    }
    public void setUser(User user) {
       this.user = user;
    }
    public String getMsg() {
       return msg;
    }
    public String execute(){
       if("xiaozhu".equals(user.getUserName())&&"111111".equals(user.getPassword()))
        msg="welcome,"+user.getUserName();
    else
        msg="用戶名或密碼錯誤";
    return this.SUCCESS;//
    }
}

響應頁面HelloWorld.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W 3C //DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        <h3><s:property value="msg"/></h3>
    </body>
</html>

新建了一個User實體類,用於封裝從表單中接收的數據,而在action中就不用設置相應的屬性和方法了,並且這樣做能夠讓我們的軟件層次感更加明顯,系統結構也更加清晰。以User爲例,描述一下采用這種方式的關鍵步驟:

1. 在action中定義一個User類型的對象:User user; 
2. 表單中各個控件的name屬性要採取如下命名規則:對象名.屬性名,注意對象名必須與action中定義的一樣,屬性名也必須和該對象的屬性一一對應。如上例中的user.userName和user.password。

注意:

public User getUser() {
       return user;
}
\Action中的這個get方法必須,否則會出現丟值的現象;set方法就更不用說了,必須的。

上述原理解釋: 

1. 前面講的自定義類型轉換器是基於 OGNL 的 DefaultTypeConverter 類並實現 convertValue() 方法,兩個轉換方向的邏輯都寫在這一個方法中。而 Struts 2 爲我們提供了一個 DefaultTypeConverter 的抽象子類 StrutsTypeConverter 來繼承,並實現其中兩個抽象方法convertFromString() 和 convertToString(),這要簡單易懂。對比 Struts 1 的轉換器是要實現org.apache.commons.beanutils.Converter 接口,以及它的 convert() 方法的。 

2. 注意,上面的 convertFromString() 的第二個參數是一個字符串數組,所以可爲請求的數組(如請求串爲 ?u=1&u=2&u=3)定義轉換器,Action 中相應的屬性就應爲數組或 List,這一方法的返回值也該爲相應的類型(數組或List,要通過第三個參數 toClass 來判斷是返回數組還是 List 了)。

3. 字符串(如 "user,pass") 轉換成 Action 中的複合屬性(如 User user) 前面是自定了類型轉換器。除此之外,還可以 Struts 2 內置的OGNL 表達式,更簡單的轉換,不用寫轉換器。例如,你的 Action 有屬性 User user,只要在 jsp 頁面的輸入框命名爲 user.name 和user.pass 即可: 
        <input type="text" name="user.name"/> 或用標籤:<s:textfield name="user.name" label="用戶名"/> 
        <input type="text" name="user.pass"/> 或用標籤:<s:textfield name="user.pass" label="密 碼"/> 
    提交後,Struts 2 即會幫你構造 User 對象(user = new User()),並賦上屬性值(user.setName(),user.setPass()),最後 user 對象賦給 Action (xxxAction.setUser(user))。所以要明白有三個必備的東西: 
        1) User 要用一個默認構造方法 2) User 要有對應 name 和 pass 的設置方法 setName() 和 setPass() 3) Action 要有 user 屬性的設置方法 setUser(),getUser() 也是要的,至於功用後面能看到。 
其實在 Struts 1 中也有這種用法,不過那是在 BeanUtils 中實現的。

4. 如果 Action 中的屬性是 Map<String, User> users; 那麼與此對應的表單寫法就是:(用標籤來寫) 
        <s:textfield name="users['one'].name" label="第一個用戶名"/> 
        <s:textfield name="users['one'].name" label="第一個密碼"/> 
        <s:textfield name="users['two'].name" label="第二個用戶名"/> 
        <s:textfield name="users['two'].name" label="第二個密碼"/> 
    應該不難想像,這個表單提交後,users 中存儲的是什麼吧! 
    如果是對於 Action 中的 List 屬性,List<User> users; 那麼與此對應的表單寫法就是: 
        <s:textfield name="users[0].name" label="第一個用戶名"/> 
        <s:textfield name="users[0].name" label="第一個密碼"/> 
        <s:textfield name="users[1].name" label="第二個用戶名"/> 
        <s:textfield name="users[1].name" label="第二個密碼"/>

5. 歸納前面3、4、5 幾點,Struts2 的 Action 在設置每一個屬性時都會 get 一下相應的元素 getUser() 或 getUsers()。 
    對於 3,在設置 user.name 和 user.pass 之前都會 getUser() 來獲取 user 屬性,如果 user 爲 null 就構造 User 對象,然後設置相應的值。假如聲明的時候就已構造好 User 對象,如有其他屬性如 age=18,並不會被覆蓋。 
   對於 4 和 5,也是在設置每一個屬性前都會調用 getUsers() 判斷聲明的 Map 或 List 是否爲 null,是則構造對應的 HashMap 或ArrayList() 對象;接着根據 Key 或下標去獲取相應位置的元素,如果不存在或爲 null 則構造之,然後設置相應屬性值。由此可見,若某元素的某個屬性未重設值則保留原值,若原來Map或List 已有多個元素,也只會改變到 Key 或索引所對應元素的某個屬性。對於 List 有可能出現跳空的情況,如頁面只有索引不從 0 開始 
         <s:textfield name="users[1].name" label="第二個用戶名"/> 
        <s:textfield name="users[1].name" label="第二個密碼"/> 
提交後就會發現,List 屬性 users 的第一個元素爲 null 了。同時如果嘗試一下,你就會發現這裏的 List 不能替代爲數組 User[] users。 
    這種樣法,可在 Struts 1 中實現,但要略施些小節,見我的另一篇日誌:提交多行數據到Struts的ActionForm的List屬性中 ,行爲表現完全一致,只是換到 Struts 2 中一切都不用自己操心。

6. 看第四點,Action 之所以知道該構造什麼類型的元素完全是由泛型告訴它的。如果不用泛型(比如用的是 JDK1.4),Action 中僅僅聲明的是 Map users; 或 List users; Action 該如何處理呢?它也不知道,只能夠幫你構造出無類型的 HashMap 和 ArrayList(),填充不了元素。這就必須在局部類型轉換的配置文件中來指定集合元素的類型。例如 Action 爲 LoginAction,就要在 LoginAction-conversion.properties 中聲明瞭,格式如下: 

    #Element_xxx=複合類型,基中 Element 是固定的,xxx 爲屬性名 
    #下面表示爲 List 屬性 users 的元素爲 com.unmi.vo.User 類型 
     Element_users=com.unmi.vo.User 

    對於 Map,須分別指定 Key 的類型和 Value 的類型 
    #Key_xxx=複合類型,基中 Key 是固定的,xxx 爲 map 屬性名,下面寫成 String 都不行的 
    Key_users=java.lang.String 
    指定 Map 的 Value 的類型與指定 List 元素類型是一樣的 
    Element_users=com.unmi.vo.User 

難怪 Struts 2 要與 1.5 以上 JDK 使用,泛型比配置來得方便。如果硬要用 1.4 JDK,就只有配置類型了,會多很多 conversion 文件的。在 提交多行數據到Struts的ActionForm的List屬性中 中類型的確定由 AutoArrayList() 的構造參數完成。

7. Set 是無序集合,所以無法像 List 那樣用數字下標來訪問,幸好 Struts 2 可爲其指定索引屬性。例如,LoginAction 聲明爲 Set users; (這裏好像用泛型只能省得了 Element_users 說明,KeyProperty_users 少不了)。則需在 LoginAction-conversion.properties 中寫下: 
    #指定 Set 的元素類型 
    Element_users=com.unmi.vo.User 

    #KeyProperty_集合屬性名=集合元素的索引屬性名,這裏爲 User 的 name 屬性 
    KeyProperty_users=name 

此時提交頁面這麼寫,最好提交前能根據輸入的用戶名自動修動輸入框的 name。 
        用戶名: <input name="users('scott').name"/> 
        密 碼: <input name="users('scott').pass"/> 
顯示的時候頁面可用標籤 
        用戶名: <s:property value="users('scott').name"/> 
        密 碼: <s:property value="users('scott').pass"/> 
注意前面,訪問 Set 元素是用的圓括號,而不同於 Map、List、數組是用中括號。我想一般也犯不着非要用 Set 而不用 List,Struts 2 中用Set 比在 Struts 1 中似乎還麻煩。

8. Struts 2 內建了一批轉換器:boolean、char、int、long、float、double 和它們的包裝類型;Date,日期格式使用請求所在 Locale的 SHORT 格式;數組,默認元素爲字符串,其他類型則要轉換每一個元素?(好像是一次性轉換完成的);集合,默認元素爲字符串XWorkList(String.class, Object[]),其他如 List<Integer> ids,類型爲 XWorkList(Integer.class, Object[]),XWorkList 繼承自 ArrayList。 

9. 類型轉換出錯由 Struts 來幫你處理,在默認攔截器棧中提供了 conversionError 攔截器,不用你寫一點代碼邏輯。conversionError 在出錯時將錯誤封裝成 fieldError,並放在 ActionContext 中。你所要做的就是遵循它的規則,1) 你的 Action 要繼承自ActionSupport,2)在 struts.xml 中聲明名爲 "input" 的 result,出錯時會在 input 邏輯視圖顯示信息。3)儘量用標籤來寫輸入域(如<s:textfield name="number" label="數量"/>),這樣轉換出錯後,就會像校驗失敗一樣把錯誤信息顯示在每個輸入框上面(視模板而定),否則要手工用 <s:fielderror/> 輸出在某處。 
默認時輸出錯誤信息爲(比如是屬性 number,輸入的是字符串時):Invalid field value for field "number".你可以改變默認顯示,在全局國際化資源文件中加上 xwork.default.invalid.fieldvalue={0}字段類型轉換失敗!。在某些時候,可能還需要對特定字段指定特別的提示信息, 那麼在名爲 ActionName.properties 的局部資源文件中加上 invalid.fieldvalue.屬性名=提示信息 (如invalid.fieldvalue.number=數量格式錯誤)

10. 最後是集合屬性轉換錯誤時的顯示,對於頁面中的同名輸入框,有多個出錯誤,如果手工用 <s:fieldError/> 只會顯示一條錯誤,但要是輸入頁是用標籤(如<s:textfield name="number" label="數量"/>),仍會在每一個出錯的輸入框上都提示。至此類型轉換的內容也就完結了。

轉自:http://hi.baidu.com/purease/item/289f6c11e551bffd9c778acc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章