本文來自:曹勝歡博客專欄。轉載請註明出處:http://blog.csdn.net/csh624366188
Web應用程序的交互都是建立在HTTP之上的,互相傳遞的都是字符串。也就是說服務器接收到的來自用戶的數據只能是字符串或者是字符數組,而在Web應用的對象中,往往使用了多種不同的類型,如整數(int)、浮點數(float)、日期(Date)或者是自定義數據類型等。因此在服務器端必須將字符串轉換成合適的數據類型。
Struts2框架中爲我們提供了一些簡單類型的轉換器,比如轉換爲int、float等簡單數據類型是不需要我們自己定義轉換器去轉換的,struts2內部本身就爲我們提供了轉換的方法,但像一些複雜的類型和我們自定義的數據類型還是需要我們自己去寫轉換器去轉換的。在轉換工程中,如果在類型轉換中出現異常,類型轉換器開發者無需關心異常處理邏輯,Struts2的conversionError攔截器會自動處理該異常,並且提示在頁面上生成提示信息。
下面我們就一步步的實現和註冊一個我們自己的轉換器,也就是自定義類型轉換器的幾個步驟:
一:自定義類型轉換器
實現自定義類型轉換器我們一般有兩種方式:
1.實現OGNL提供的TypeConvert接口以及實現了TypeConvert接口的DefaultTypeConvert類來實現自定義的類型轉換器。我們來看一下DefaultTypeConvert類的源碼:
- publicclass DefaultTypeConverter implements TypeConverter{
- public DefaultTypeConverter(){
- super();
- }
- /**
- * @param context:類型轉換的上下文
- * @param value:需要轉換的參數
- * @param toType:轉換後的目的類型
- */
- public Object convertValue(Map context,
- Object value,
- Class toType)
- {
- return OgnlOps.convertValue(value, toType);
- }
- public Object convertValue(Map context, Object target,
- Member member, String propertyName,
- Object value, Class toType)
- {
- return convertValue(context, value, toType);
- }
- }
publicclass DefaultTypeConverter implements TypeConverter{
public DefaultTypeConverter(){
super();
}
/**
* @param context:類型轉換的上下文
* @param value:需要轉換的參數
* @param toType:轉換後的目的類型
*/
public Object convertValue(Map context,
Object value,
Class toType)
{
return OgnlOps.convertValue(value, toType);
}
public Object convertValue(Map context, Object target,
Member member, String propertyName,
Object value, Class toType)
{
return convertValue(context, value, toType);
}
}
convertValue方法的作用:
該方法負責完成類型的雙向轉換,爲了實現雙向轉換,我們通過判斷toType的類型即可判斷轉換的方向。toType類型是需要轉換的目標類型,如:當toType類型是User類型時,表明需要將字符串轉換成User實例;當toType類型是String類型時,表明需要把User實例轉換成字符串類型。通過toType類型判斷了類型轉換的方向後,我們就可以分別實現兩個方向的轉換邏輯了。實現類型轉換器的關鍵就是實現conertValue方法,該方法有三個參數:
第一個參數 context:類型轉換的上下文
第二個參數 value:需要轉換的參數
第三個參數 toType:轉換後的目的類型
2. 基於Struts2的類型轉換器
Struts 2提供了一個StrutsTypeConverter的抽象類,這個抽象類是DefaultTypeConverter類的子類。開發時可以直接繼承這個類來進行轉換器的構建。通過繼承該類來構建類型轉換器,可以不用對轉換的類型進行判斷(和第一種方式的區別),下面我們來看一下StrutsTypeConverter類的源碼:
- publicabstractclass StrutsTypeConverter extends DefaultTypeConverter {
- //重寫DefaultTypeConverter類的convertValue方法
- public Object convertValue(Map context, Object o, Class toClass) {
- //如果需要把複合類型轉換成字符串類型
- if (toClass.equals(String.class)) {
- return convertToString(context, o);
- }
- //如果需要把字符串轉換成符合類型
- elseif (o instanceof String[]) {
- return convertFromString(context, (String[]) o, toClass);
- }
- //如果需要把字符串轉換成符合類型
- elseif (o instanceof String) {
- return convertFromString(
- context, new String[]{(String) o}, toClass);
- } else {
- return performFallbackConversion(context, o, toClass);
- }
- }
- protected Object performFallbackConversion(Map context,
- Object o, Class toClass) {
- returnsuper.convertValue(context, o, toClass);
- }
- publicabstract Object convertFromString(Map context,
- String[] values, Class toClass);
- publicabstract String convertToString(Map context, Object o);
- }
publicabstractclass StrutsTypeConverter extends DefaultTypeConverter {
//重寫DefaultTypeConverter類的convertValue方法
public Object convertValue(Map context, Object o, Class toClass) {
//如果需要把複合類型轉換成字符串類型
if (toClass.equals(String.class)) {
return convertToString(context, o);
}
//如果需要把字符串轉換成符合類型
elseif (o instanceof String[]) {
return convertFromString(context, (String[]) o, toClass);
}
//如果需要把字符串轉換成符合類型
elseif (o instanceof String) {
return convertFromString(
context, new String[]{(String) o}, toClass);
} else {
return performFallbackConversion(context, o, toClass);
}
}
protected Object performFallbackConversion(Map context,
Object o, Class toClass) {
returnsuper.convertValue(context, o, toClass);
}
publicabstract Object convertFromString(Map context,
String[] values, Class toClass);
publicabstract String convertToString(Map context, Object o);
}
該類已經實現了DefaultTypeConverter的convertValue方法。實現該方法時,它將兩個不同轉換方向替換成不同方法——當需要把字符串轉換成複合類型時,調用convertFromString抽象方法;當需要把複合類型轉換成字符串時,調用convertToString抽象方法,下圖展示了其對應關係:
二.註冊自定義類型轉換器:
實現了自定義的類型轉換器之後,將該類型轉換器註冊在Web應用中,Struts2框架纔可以正常使用該類型轉換器,類型轉換器的註冊分爲兩種,
1.註冊局部類型轉換器。
局部類型轉換器僅僅對某個Action起作用。局部類型轉換器非常簡單,只需要在相應的Action目錄下新建一個資源文件。該資源文件名格式如下。ActionName-conversion.properties。其中ActionName表示需要進行轉換的Action的類名,“-conversion.properties”字符串則是固定格式的。該文件也是一個典型Properties文件,文件由鍵值對組成:propertyName = 類型轉換器類
如:name=util.NameConvert
name:表示要進行轉換的屬性
util.NameConvert:表示要進行轉換的自定義類型轉換器。
注意:該屬性文件應該與ActionName.class放在相同位置。
2.註冊全局類型轉換器。對所有Action的特定類型的屬性都會生效。
全局類型轉換器,必須提供一個xwork-conversion.properties文件。文件必須保存在classes目錄下。該資源文件名格式如下:
複合類型=對應的類型轉換器
複合類型:指定需要完成類型轉換的複合類
對應的類型轉換器:指定所指定類型轉換的轉換器。
如:註冊User類的全局類型轉換器爲:UserConverter
cn.wjz.bean.User = cn.wjz.util.UserConverter
注意:如果局部類型轉換和全局類型轉換同時存在的話,局部類型轉換具有較高的優先級,也就是以局部類型轉換器爲主。
三.集合類型的類型轉換
對於List元素來說,內容如: Element_attributeName=typeName;
對於Map元素來說,
(1)如果表示key的類型,則:Key_attributeName=typeName;
(2)如果表示value的類型,則爲:Element_attributeName=typeName;
比如,此處沒有使用泛型,而是使用了局部類型轉換文件:
- Conversion02Action.java
- public class Conversion02Action extends ActionSupport {
- private List lists;
- private Map maps;
- public String execute()throws Exception{
- System.out.println(((Person)lists.get(0)).getGender());
- System.out.println(((Person)lists.get(0)).getSalary());
- System.out.println(((Person)maps.get("one")).getGender());
- System.out.println(((Person)maps.get("one")).getSalary());
- return SUCCESS;
- }
- public List getLists() {
- return lists;
- }
- public void setLists(List lists) {
- this.lists = lists;
- }
- public Map getMaps() {
- return maps;
- }
- public void setMaps(Map maps) {
- this.maps = maps;
- }
- }
Conversion02Action.java
public class Conversion02Action extends ActionSupport {
private List lists;
private Map maps;
public String execute()throws Exception{
System.out.println(((Person)lists.get(0)).getGender());
System.out.println(((Person)lists.get(0)).getSalary());
System.out.println(((Person)maps.get("one")).getGender());
System.out.println(((Person)maps.get("one")).getSalary());
return SUCCESS;
}
public List getLists() {
return lists;
}
public void setLists(List lists) {
this.lists = lists;
}
public Map getMaps() {
return maps;
}
public void setMaps(Map maps) {
this.maps = maps;
}
}
Conversion02Action-conversion.properties
- Element_lists=org.person.Person
- Key_maps=java.lang.String
- Element_maps=org.person.Person
Element_lists=org.person.Person
Key_maps=java.lang.String
Element_maps=org.person.Person
頁面表單:
- <s:fielderror></s:fielderror>
- <s:form action="conversion02" >
- <s:textfield label="list1.salary" name="lists[0].salary"></s:textfield>
- <s:textfield label="list1.gender" name="lists[0].gender"></s:textfield>
- <s:textfield label="map1.gender" name="maps['one'].gender"></s:textfield>
- <s:textfield label="map1.salary" name="maps['one'].salary"></s:textfield>
- <s:submit value="提交"></s:submit>
- </s:form>
<s:fielderror></s:fielderror>
<s:form action="conversion02" >
<s:textfield label="list1.salary" name="lists[0].salary"></s:textfield>
<s:textfield label="list1.gender" name="lists[0].gender"></s:textfield>
<s:textfield label="map1.gender" name="maps['one'].gender"></s:textfield>
<s:textfield label="map1.salary" name="maps['one'].salary"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
四.Struts 2內建的類型轉換器 :
Sturts 2爲常用的數據類型提供了內建的類型轉換器,所以根本不用自定義轉換器。對於內建的轉換器,Struts在遇到這些類型時,會自動去調用相應的轉換器進行類型轉換。
Struts 2全部的內建轉換器:
·基本數據類型以及其封裝類。包括:boolean和Boolean、char和Character、int和Integer、long和Integer、float和Float、double和Double。完成字符串和基本數據類型或其封裝類之間的轉換。
·日期類型。使用當前區域的短格式轉換,即DateFormat.getInstance(DateFormat.SHORT)完成字符串和日期類型之間的轉換。
·集合(Collection)類型。將request.getParameterValues(String arg)返回的字符串數據與java.util.Collection轉換。集合元素爲String類型。
·集合(Set)類型。與Collection的轉換相似,只是去掉了相同的值。集合元素爲String類型。
·數組類型。將request.getParameterValues(String arg)返回的字符串數組中的每個字符串值取出組成一個數組。數組元素爲String類型。
注意:Struts 2提供的全部內建轉換器都是雙向的,也就是說從用戶輸入頁到服務器端時會將字符串類型轉換成相應的數據類型。在顯示輸出時,又會將相應的數據類型轉換成字符串類型來顯
數組類型的轉換器。這個轉換器非常有用,比如多個表單元素的name屬性相同,那麼提交的參數就不再是字符串而是一個字符串數組。通過Sturts 2提供的數組類型的轉換器就能很方便的將多個相同name屬性的表單元素的值封裝到Action中的一個數組中。
五.類型轉換中錯誤處理:
1.類型轉換的錯誤處理流程:
Struts2提供了一個名爲conversionError的攔截器,這個攔截器被註冊在默認的攔截器棧中,在Struts-default.xml中的配置信息:
- <interceptor-stack name="defaultStack">
- .. .. ..
- <!—- 處理類型轉換錯誤的攔截器 -->
- <interceptor-ref name="conversionError"/>
- <!—- 處理數據校驗的攔截器 -->
- <interceptor-ref name="validation">
- <param name="excludeMethods">input,back,cancel,browse</param>
- </interceptor-ref>
- <interceptor-ref name="workflow">
- <param name="excludeMethods">input,back,cancel,browse</param>
- </interceptor-ref>
- </interceptor-stack>
<interceptor-stack name="defaultStack">
.. .. ..
<!—- 處理類型轉換錯誤的攔截器 -->
<interceptor-ref name="conversionError"/>
<!—- 處理數據校驗的攔截器 -->
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
如果Struts2的類型轉換器執行類型轉換時出現錯誤,該攔截器將負責將對應的錯誤封裝成表單域錯誤(fieldError),並將這些錯誤信息放入ActionContext中。
Struts2的錯誤處理流程:
2、錯誤信息的友好顯示
在進行類型轉換中,如果出現錯誤將會提示錯誤信息。Struts 2默認提供了錯誤信息提示,但是這些錯誤信息提示不夠友好,下面將介紹如何自定義錯誤信息來取代Struts 2的默認錯誤信息。
·定義全局類型轉換錯誤處理信息:
在應用的國際化資源文件中增加如下的信息:
xwork.default.invalid.fieldvalue = key
key的值就是用戶希望在頁面中顯示的提示信息。 例如:
#改變默認的類型轉換失敗後的提示信息 xwork.default.invalid.fieldvalue = {0}字段類型轉換失敗!! 因爲包含非西歐字符,因此使用 native2ascii 命令處理 xwork.default.invalid.fieldvalue = {0}\u5b57\u6bb5\u7c7b\u578b\u8f6c\u6362\u5931\u8d25\uff01\uff01 |
·定義局部類型轉換錯誤處理信息:
在某些時候可能還需要對特定的字段指定特別的提示信息,此時可以提供該Action的局部資源文件,文件名:ActionName.properties , 在文件中增加如下一項:
invalid.fieldvalue.屬性名 =提示信息
例如:
#改變Action中birth屬性類型轉換錯誤後的提示信息 invalid.fieldvalue.birth = 生日信息必須滿足yyyy-MM-DD格式 使用 native2ascii 命令處理 invalid.fieldvalue.birth = \u751f\u65e5\u4fe1\u606f\u5fc5\u987b\u6ee1 \u8db3yyyy-MM-DD\u683c\u5f0f
|
六.類型轉換的流程
1、用戶進行請求,根據請求名在struts.xml中尋找Action
2、在Action中,根據請求域中的名字去尋找對應的set方法。找到後在賦值之前會檢查這個屬性有沒有自定義的類型轉換。沒有的話,按照默認進行轉換;如果某個屬性已經定義好了類型轉換,則會去檢查在Action同一目錄下的action文件名-conversion.properties文件。
3、從文件中找到要轉換的屬性及其轉換類。
4、然後進入轉換類中,在此類中判斷轉換的方向。我們是先從用戶請求開始的,所以這時先進入從字符串到類的轉換。返回轉換後的對象。流程返回Action。
5、將返回的對象賦值給Action中的屬性,執行Action中的execute()
6、執行完execute()方法,根據struts.xml的配置轉向頁面
7、在jsp中顯示內容時,根據頁面中的屬性名去調用相應的get方法,以便輸出
8、在調用get方法之前,會檢查有沒有此屬性的自定義類型轉換。如果有,再次跳轉到轉換類當中。
9、在轉換類中再次判斷轉換方向,進入由類到字符串的轉換,完成轉換後返回字符串。
10、將返回的值直接帶出到要展示的頁面當中去展示。