1、自動類型轉換
Struts2內部提供大量類型轉換器,用來完成數據類型轉換問題:
- String和boolean、Boolean:完成字符串與布爾值之間的轉換
- String和char、Character:往常字符串與字符之間的轉換
- String和int、Integer:完成字符串與整型之間的轉換
- String和long、Long:完成字符串與長整型值之間的轉換
- String和double、Double:完成字符串與雙精度浮點值的轉換
- String和float、Float:完成字符串和單精度浮點之間的轉換
- String和Date:完成字符串和日期類型之間的轉換,可以接收yyyy-MM-dd格式字符串
- String和數組:可以將多個同名參數,轉換到數組中
- String和Map、List:支持將數據保存到List或者Map集合
自動類型轉換例子:
表單信息:
1 <form action="${pageContext.servletContext.contextPath}/converterAction.action"> 2 name:<input type="text" name="name"><br> 3 password:<input type="password" name="password"><br> 4 age:<input type="text" name="age"><br> 5 birthday:<input type="text" name="birthday"><br> 6 hobby:<input name="hobby" type="checkbox" value="music">music 7 <input name="hobby" type="checkbox" value="movie">movie<br> 8 <input type="submit" value="提交"> 9 </form>
Action類:
1 public class ConverterAction extends ActionSupport { 2 private String name; 3 private String password; 4 private int age; 5 private Date birthday; 6 private String[] hobby; 7 8 public String getName() { 9 return name; 10 } 11 public void setName(String name) { 12 this.name = name; 13 } 14 public String getPassword() { 15 return password; 16 } 17 public void setPassword(String password) { 18 this.password = password; 19 } 20 public int getAge() { 21 return age; 22 } 23 public void setAge(int age) { 24 this.age = age; 25 } 26 public Date getBirthday() { 27 return birthday; 28 } 29 public void setBirthday(Date birthday) { 30 this.birthday = birthday; 31 } 32 public String[] getHobby() { 33 return hobby; 34 } 35 public void setHobby(String[] hobby) { 36 this.hobby = hobby; 37 } 38 39 @Override 40 public String execute() throws Exception { 41 System.out.println("name: " + name); 42 System.out.println("password: " + password); 43 System.out.println("age: " + age); 44 System.out.println("birthday: " + birthday); 45 System.out.print("hobby: "); 46 for (int i = 0; i < hobby.length; i++) { 47 System.out.print(hobby[i]); 48 if (i != hobby.length - 1) { 49 System.out.print(", "); 50 } 51 } 52 return SUCCESS; 53 } 54 }
前台輸入信息:
后台顯示信息:
2、自定義類型轉換
1)基於OGNL的類型轉換器
Struts2的類型轉換器實際上是基於OGNL實現的,都需要實現一個TypeConverter接口,該接口位於ognl.jar包內。該接口定義了一個convertValue()方法,實現該接口的類型轉換器實現類都需要重寫該方法來進行類型轉換。
1 public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);
由於TypeConverter接口的convertValue()方法過於復雜,OGNL還提供了一個實現TypeConverter接口的類DefaultTypeConverter,開發者只要繼承該類,就可以開發類型轉換器的實現類。DefaultTypeConverter類的子類需要重寫convertValue()方法,來實現字符串類型與復合類型之間的雙向轉換,convertValue()方法有三個參數:Map context:該參數為類型轉換環境的上下文內容;Object value:該參數為需要轉換的參數,是一個字符串數組;Class toType:該參數指的是轉換目標的類型。
在Struts2中並沒有直接使用ognl.DefaultTypeConverter實現類,甚至都沒有使用ognl.TypeConverter接口,而是自己定義了一個與ognl.TypeConverter接口一樣的TypeConverter接口,該接口位於com.opensymphony.xwork2.conversion包下,其名稱和接口函數定義完全相同。Struts2自行定義TypeConverter接口的目的在於對外屏蔽類型轉換的實現細節,從而能夠將Struts2對TypeConverter的擴展實現納入到Struts2的容器中進行管理,從而方便對OGNL原始的TypeConverter接口進行擴展並支持更加廣泛的類型轉換邏輯。
在Struts2的com.opensymphony.xwork2.conversion包下DefaultTypeConverter實現類有3個convertValue方法:
所以我們在繼承com.opensymphony.xwork2.conversion包下DefaultTypeConverter實現類的時候可以直接重寫convertValue(Object value, Class toType)方法即可。
無論是繼承ongl包下的DefaultTypeConverter還是繼承com.opensymphony.xwork2.conversion包下DefaultTypeConverter,在自定義類型轉換的時候,實現方式都一樣。
2)基於Struts2的類型轉換器
Struts2框架提供了一個類型轉換器的StrutsTypeConverter抽象類,可以繼承該類來開發自定義的類型轉換器實現類。 該抽象類實際上繼承了DefaultTypeConverter類,在該類的基礎上進行了簡化。StrutsTypeConverter類中提供了兩個抽象方法,分別實現"form字符串參數-Struts復合類型"之間的雙向轉換。
3、注冊類型轉換器
在Struts2框架中使用自定義類型轉換器需要注冊,這樣Struts2框架在處理用戶請求的時候才知道使用哪個類型轉換器進行轉換。Struts2有兩種方式注冊類型轉換器:
1)注冊局部類型轉換器
局部類型轉換器僅僅對某個Action的屬性其作用,注冊局部類型轉換器需要建立一個命名規則為ActionName-conversion.properties的屬性文件,該屬性文件保存在與Action類文件相同的目錄下。ActionName就是使用類型轉換器的Action類的類名,而-conversion.properties是固定的格式。該文件是一個標准的屬性文件,內容為標准的Key-Value格式,該鍵值對定義如下:
1 <!--propertyName為要進行轉換的屬性名--> 2 propertyName=類型轉換器
如果在model中有兩個類User和Product,它們都有Date類型屬性,都需要在頁面上輸入,但是它們格式不一樣,例如:
User birthday 格式:yyyy/MM/dd
Product producttime 格式:yyyy-MM-dd
如果遇到這種情況,可以在model所在的包下創建注冊的properties文件,名稱的命名規則為modelname-conversion.properties,該屬性文件的內容和上面的一樣:
1 <!--propertyName為要進行轉換的屬性名--> 2 propertyName=類型轉換器
2)注冊全局類型轉換器
全局類型轉換器對所有Action的特定屬性都會生效,注冊一個全局類型轉換器,需要建立一個xwork-conversion.properties屬性文件,該文件需要保存在class路徑的根目錄下,如WEB-INF/classes。該文件的內容為"復合類型-對應的類型轉換器",其中復合類型就是Action中需要進行類型轉換的屬性所屬於的類型,對應的類型轉換器就是轉換該復合類型的對應轉換器,比如要對Date類型的屬性進行轉換:
java.util.Date=com.sunny.converter.DateConverter
4、類型轉換器的實現
在類型轉換器的實現例子中自定義類型轉換器繼承的是DefaultTypeConverter,注冊局部類型轉換器。
表單信息:
1 <form action="${pageContext.servletContext.contextPath}/converterAction.action"> 2 name:<input type="text" name="name"><br> 3 password:<input type="password" name="password"><br> 4 age:<input type="text" name="age"><br> 5 birthday:<input type="text" name="birthday"><br> 6 hobby:<input name="hobby" type="checkbox" value="music">music 7 <input name="hobby" type="checkbox" value="movie">movie<br> 8 <input type="submit" value="提交"> 9 </form>
自定義類型轉換器類:
1 public class DateConverter extends DefaultTypeConverter { 2 @Override 3 public Object convertValue(Object value, Class toType) { 4 if (value == null || toType == null) { 5 return false; 6 } 7 8 if (toType != Date.class) { 9 return false; 10 } 11 12 /* 13 * 對於DefaultTypeConverter轉換器而言,它必須考慮到最通用的情形, 14 * 因此他把所有請求參數都視為字符串數組而不是字符串。 15 */ 16 if (value instanceof String[]) { 17 String str[] = (String[])value; 18 if (str[0] != null && str[0].length() > 0) { 19 SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); 20 try { 21 //把請求參數轉換成特定格式的date類 22 return sdf.parse(str[0]); 23 } catch (ParseException e) { 24 /* 25 * 在struts2框架里,自定義的類型轉換器, 26 * 如果我們不手動拋出異常,struts2框架只捕獲異常,但是並不拋出。 27 * 所以框架就會認為類型轉換器轉換成功,轉向成功頁面。 28 */ 29 throw new RuntimeException(e); 30 } 31 } 32 } 33 return new Date(); 34 } 35 }
注冊局部類型轉換器,對Date類型的birthday進行轉換,接收yyyy/MM/dd類型字符串:
1 birthday=com.sunny.converter.DateConverter
Action類:
1 public class ConverterAction extends ActionSupport { 2 private String name; 3 private String password; 4 private int age; 5 private Date birthday; 6 private String[] hobby; 7 8 public String getName() { 9 return name; 10 } 11 public void setName(String name) { 12 this.name = name; 13 } 14 public String getPassword() { 15 return password; 16 } 17 public void setPassword(String password) { 18 this.password = password; 19 } 20 public int getAge() { 21 return age; 22 } 23 public void setAge(int age) { 24 this.age = age; 25 } 26 public Date getBirthday() { 27 return birthday; 28 } 29 public void setBirthday(Date birthday) { 30 this.birthday = birthday; 31 } 32 public String[] getHobby() { 33 return hobby; 34 } 35 public void setHobby(String[] hobby) { 36 this.hobby = hobby; 37 } 38 39 @Override 40 public String execute() throws Exception { 41 System.out.println("name: " + name); 42 System.out.println("password: " + password); 43 System.out.println("age: " + age); 44 System.out.println("birthday: " + birthday); 45 System.out.print("hobby: "); 46 for (int i = 0; i < hobby.length; i++) { 47 System.out.print(hobby[i]); 48 if (i != hobby.length - 1) { 49 System.out.print(", "); 50 } 51 } 52 return SUCCESS; 53 } 54 }
前台輸入信息:
后台顯示信息:
注意事項:
- 在進行自定義類型轉換時,如果轉換錯誤,我們不手動拋出異常,struts2框架只捕獲異常,並不拋出,框架就會認為轉換成功,轉向成功頁面,所以一定要手動拋出異常。
- 不管是自動類型轉換還是自定義類型轉換,如果類型轉換錯誤,會跳轉到input視圖,如果在struts.xml文件中沒有name值為"input"的<result>,會跳轉到錯誤頁面。
5、錯誤處理機制
Struts2的錯誤處理是由conversionError攔截器自動完成的,當發生類型轉換錯誤時,conversionError攔截器攔截此錯誤並封裝成fieldError,將此錯誤信息放入ActionContext中,並返回input邏輯視圖,我們前面提到過此時必須在struts.xml文件中有name值為"input"的<result>,在jsp頁面中可以使用<s:fielderror/>即可顯示錯誤信息,默認顯示的錯誤信息是在屬性文件xwork-messages.properties中定義的:
1 xwork.error.action.execution=Error during Action invocation 2 xwork.exception.missing-action=There is no Action mapped for action name {0}. 3 xwork.exception.missing-package-action=There is no Action mapped for namespace {0} and action name {1}. 4 xwork.default.invalid.fieldvalue=Invalid field value for field "{0}".
當birthday輸入格式錯誤時,根據action中的<result name="input">/error.jsp</result>會跳轉到error.jsp頁面,
jsp頁面信息:
1 <body> 2 <s:fielderror fieldName="birthday"/> 3 </body>
jsp頁面顯示信息:
1 <struts> 2 <constant name="struts.devMode" value="true" /> 3 <constant name="struts.custom.i18n.resources" value="com.sunny.action.converter" /> 4 <package name="default" namespace="/" extends="struts-default"> 5 <action name="converterAction" class="com.sunny.action.ConverterAction"> 6 <result>/success.jsp</result> 7 <result name="input">/error.jsp</result> 8 </action> 9 </package> 10 11 </struts>
說明:資源文件的定義有好幾種方式,會在國際化中講到
如果birthday輸入格式錯誤,顯示如下:
