Struts2學習筆記(十二) 型別轉換(Type Conversion)(下)
null引用處理
我們知道,我們在Action中定義屬性時並沒有對他們進行初始化,那麼也就是Struts2在對請求引數進行型別轉換時,我們的Action屬性可能還是null。那麼框架會自動將這些null的屬性例項化一個預設的物件(在學習Parameters攔截器時已經看過原始碼了):
(1) 如果我們的屬性宣告為List(Collection)型別,那麼預設會給他例項化一個ArrayList物件
(2) 如果我們的屬性宣告為Map型別,那麼預設會給他例項化一個HashMap物件
(3) 如果我們的屬性是一個簡單的JavaBean,並且含有一個沒有引數的構造器,那麼Struts2會通過ObjectFactory類的buildBean方法來給他例項化一個對應的物件
自定義型別轉換器
要想實現自定義型別轉換器,只需我們自定義的Class實現ognl.TypeConterter介面即可。Strtus2中的DefaultTypeConverter類已經實現了這個介面,並且其子類StrutsTypeConverter對它做了更好的擴充,這是一個抽象類,需要我們繼承並實現其中的兩個方法。
(Map context,String[] values,
Class toClass) |
(Map context,Object |
這樣使得不用我們自己去判斷轉換的方向。 那麼要實現自定義的型別轉換器,選擇實現StrutsTypeConverter類顯得最方便。下面我們就動手自己寫一個自定義型別轉換器,來實現將字串轉換為User物件。
User.java
public class User { private String userName; private int age; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
UserAction.java
public class UserAction extends ActionSupport {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
return SUCCESS;
}
}
UserConverter.java
public class UserConverter extends StrutsTypeConverter {
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
String[] input = values[0].split(":");
if(input.length != 2){
throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
}
try {
User user = new User();
String name = input[0];
int age = Integer.parseInt(input[1]);
user.setUserName(name);
user.setAge(age);
return user;
} catch (Exception e) {
throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
}
}
@Override
public String convertToString(Map context, Object o) {
User user = (User) o;
String str = "Name : "+user.getUserName()+";Age : "+user.getAge();
return null;
}
}
input.jsp
<body>
<s:iftest="hasFieldErrors()">
<s:iteratorvalue="fieldErrors">
<fontcolor="#FF0000"><s:propertyvalue="value[0]"/></font><br>
</s:iterator>
</s:if>
<formaction="user.action"method="post">
user: <inputtype="text"name="user"/>
<inputtype="submit"value="submit"/>
</form>
success.jsp
<body>
${user.userName }<br/>
${user.age }
</body>]
現在程式碼就寫完了,剩下的就是要來註冊我們的型別轉換器了。我們需要在Action類所在目錄下新建一個”ActionClass-conversion.properties”形式的檔案,並在其中為Action的屬性配置型別轉換器,格式為:屬性名=型別轉換器類全名(好包名)。例如本例中需要在UserAction類所在目錄下新建一個UserAction-conversion.properties檔案,內容如下:
user =action.UserConverter
測試:
提交:
定製錯誤訊息
這裡我們在表單中都是按照我們預定的格式輸入,那麼如果我們輸入的資料並不是按照我們規定的格式,那麼會是什麼結果呢?我們將輸入改為”zhangsan-21”,再次提交:
這樣的錯誤訊息可能並不是我們想要的,那麼我們怎麼去定製這些錯誤訊息呢。還記得我們在學習Action的時候學到的關於validate方法驗證失敗時的錯誤訊息定製吧。在這裡我們同樣可以使用這種方法。只需要在我們的ActionClass.properties檔案中按照指定的格式配置就可以了:
invalid.fieldvalue.fieldName= 自定義訊息
這個檔案也需要和Action類放在同一目錄。例如本例中我們在UserAction.properties檔案中為user成員屬性新增一條型別轉換錯誤訊息:
invalid.fieldvalue.user=\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u683C\u5F0F\u5982\uFF0Czhangsan\:21(內容為:請輸入正確的格式如,zhangsan:21)
還記得吧,訊息的內容都必須是unicode編碼的。
現在我們再來測試,輸入”zhangsan-21”,提交:
非Action的屬性的自定義型別轉換
除了Action的成員屬性以外,我們還可以給其他類的成員變數定義自定義型別轉換器。比如User類中有一個Address型別的屬性,Address是我們自定義的類。那麼我們可以為User類中的Address屬性定義一個型別轉換器。和Action一樣,我們需要在這個類所在的目錄下新建一個ClassName-conversion.properties的檔案,其中內容定義的格式和上面講的一樣。
全域性型別轉換器
有時候我們定義的類(bean/model)並不只是在一個Action中會被用到,那麼如果需要定義自定義型別轉換器的話,如果為每個Action都建立一個ActionClass-conversion.properties來完成對該型別的轉換,顯得很不靈活。Strtus2還提供了全域性的型別轉換配置,它的配置不再是針對類的屬性,而是正對類本身。我們需要在src(classpath)下新建一個名為xwork-conversion.properties的檔案。檔案的內容和為action屬性定義型別轉換器的格式相似,只不過key需要是類的全路徑。例如:
bean.User = action.UserConverter
這樣我們在多個Action中使用該型別的屬性時就可以不用在一一的配置型別轉換器了。
關於List和Map型別的型別轉換
在上一篇中我們討論過如何讓Struts2來完成List/Map型別的屬性的自動型別轉換,但是條件是List/Map中的物件型別必須是Struts2內建型別轉換器能夠完成轉換的型別,那麼如果List/Map中的物件是我們自定義的資料型別,那麼該怎麼讓Struts2來完成轉換呢。
首先我們要告訴Struts2,List/Map型別屬性中元素的型別,通過在ActionClass-conversion.properties檔案中使用如下形式:
List: Element_Action中List型別屬性名 = List中元素型別(類的全路徑)
Map:key: Key_Action中Map型別屬性名 = Map中Key的型別(類的全路徑)
Value Element_Action中Map型別屬性名 = Map中Value的型別(類的全路徑)
struts2的文件中只介紹了這些。我疑惑的地方就是根據文件中的介紹,我們只是告訴了Struts2集合中元素的型別,但是並沒告訴Struts2如何將請求引數的String型別轉換為這些指定的型別。但是我按照文件中的說法來做了,發現轉換並不能成功。由於集合中元素的型別並沒有包含在Action的屬性中,那麼肯定不能在ActionClass-conversion.properties中配置型別轉換器了。那麼就只有為這個類配置一個全域性轉換器了,配置了型別轉換器之後,Map型別可成功轉換。
例子程式碼如下:
User.java略
UserAction.java
public class UserAction extends ActionSupport {
private List<User>users;
public List<User> getUsers() {
returnusers;
}
public void setUsers(List<User> users) {
this.users = users;
}
@Override
public String execute()throws Exception {
System.out.println(users);
returnSUCCESS;
}
}
UserAction-conversion.properties
Element_users = bean.User
CreateIfNull_users=true
xwork-conversion.properties
bean.User =action.UserConverter
UserConverter.java
public class UserConverter extends StrutsTypeConverter {
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
String[] input = values[0].split(":");
if(input.length != 2){
throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
}
try {
User user = new User();
String name = input[0];
int age = Integer.parseInt(input[1]);
user.setUserName(name);
user.setAge(age);
return user;
} catch (Exception e) {
throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
}
}
@Override
public String convertToString(Map context, Object o) {
User user = (User) o;
String str = "Name : "+user.getUserName()+";Age : "+user.getAge();
return null;
}
}
input.jsp
<body>
<s:iftest="hasFieldErrors()">
<s:iteratorvalue="fieldErrors">
<fontcolor="#FF0000"><s:propertyvalue="value[0]"/></font><br>
</s:iterator>
</s:if>
<formaction="user.action"method="post">
user: <inputtype="text"name="users[0]"/>
<inputtype="submit"value="submit"/>
</form>
</body>
success.jsp
<body>
${users[0].userName}<br/>
</body>
測試:
結果:
備註:我們不能將自定義的interface作為Action的屬性,這是因為Struts2並不知道該給它例項化一個什麼樣的物件。還有就是在使用泛型的時候要注意型別擦出問題。
總結:在List/Map這個問題上不知道我理解的是否正確。如果理解的有問題還請路過的前輩指正一下啊!學了型別轉換之後,覺得自定義型別轉換器還真是麻煩,感覺沒啥必要,我想應該沒幾個人將User的name和age放在一個input中吧,更何況還是需要按照指定的格式來。使用系統內建的型別轉換器就好了,最多不過在表單上多做一點。不過學習一下型別轉換還是對理解Struts2框架有幫助的。這裡還忘了一個攔截器了,Conversion Error Interceptor,他會將從ActionContext中conversionErrors屬性中的所有錯誤資訊新增為Actin的field errors,這也正是我可以在Input頁面使用hasFieldErrors等獲取field error的方式來獲取型別轉換錯誤資訊的原因。