一、Struts2 類型轉換介紹
類型轉換:解析HTTP請求參數,將Http請求參數賦值給Action的屬性;比如:
<s:form action="valid" >
<s:textfield label="用戶名" name="name"></s:textfield>
<s:password label="密碼" name="password"></s:password>
<s:textfield label="年齡" name="age"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
此處將名為name、password、age的值賦值給Action的屬性,類型轉換是指這個賦值過程中可能出現的類型不一致問題而做出的轉換,因為HTTP參數都是字符串類型的;因此從String類型的age參數傳給int類型的age屬性時,就需要類型轉換,當然這些struts2框架已經做完瞭,開發人員不需要自行處理;
Struts2已經內建瞭對於String和基本類型的類型轉換,比如String<–>int 、String<–>boolean等;
Action屬性一定要有getter和setter方法!
Struts2提供瞭強大的類型轉換支持,不僅提供瞭內置的類型轉換器,還可以自定義類型轉換器,以滿足自定義需求;
Struts2類型轉換是通過params攔截器進行轉換;如果轉換失敗,則conversionError攔截器攔截該異常,並封裝到fieldError中,放入ActionContext中;
Struts2類型轉換還支持OGNL表達式,隻需要在表單控件的name屬性使用ognl表達式即可,比如表單控件的name屬性為user.name,則此控件的數據將進入賦值給Action的user屬性中的name屬性;
二、通過一個例子初步瞭解Struts2類型轉換
此處的類型轉換因為是內建的,因此代碼上和原來的struts2應用沒有不同,struts2框架內部完成瞭所有瞭類型轉換,以下程序是將name、age賦值給Action類的name、age屬性,gender、salary賦值給p.gender、p.salary屬性;struts.xml的配置和原來一樣,所以就不給出瞭;
Conversion01Action.java
package org.conversion.action;
import org.person.Person;
import com.opensymphony.xwork2.ActionSupport;
public class Conversion01Action extends ActionSupport {
private String name;
private int age;
private Person p ;
public String execute()throws Exception{
System.out.println(name);
System.out.println(age);
System.out.println(p.getGender());
System.out.println(p.getSalary());
return SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
}
1.jsp
此處需要註意的是性別、薪水的表單控件的name屬性是OGNL表達式;
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<s:fielderror></s:fielderror>
<s:form action="conversion01" >
<s:textfield label="姓名" name="name"></s:textfield>
<s:textfield label="年齡" name="age"></s:textfield>
<s:textfield label="性別" name="p.gender"></s:textfield>
<s:textfield label="薪水" name="p.salary"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
</body>
</html>
補充:List 和Map 的Action屬性的類型轉換問題
前面我們將的都是基本類型和自定義類型,因此這裡我們將講述集合類型的類型轉換問題;
如果Action的屬性為List類型,List<String>lists ,則表單控件的命名方式為lists[i] ,如lists[0]表示第1個元素;
如果Action的屬性為Map類型,Map<String,Person> maps;則表單控件的命名方式為maps[ ' first ' ].salary ,表示map中key為'first'的value中的salary屬性;
以上的list和map類型屬性都通過泛型的方式說明集合元素的類型,如果在JDK1.5之前,泛型沒有出現,則需要通過局部類型轉換文件進行表示;
局部類型轉換文件:對於特定Action的特定屬性有效,位於特定Action同一目錄下,命名規則:ActionName-conversion.properties;
對於List元素來說,內容如 :Element_attributeName=typeName;
對於Map元素來說,
(1)如果表示key的類型,則:Key_attributeName=typeName;
(2)如果表示value的類型,則為:Element_attributeName=typeName;
比如,此處沒有使用泛型,而是使用瞭局部類型轉換文件:
Conversion02Action.java
package org.conversion.action;
import java.util.List;
import java.util.Map;
import org.person.Person;
import com.opensymphony.xwork2.ActionSupport;
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
2.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<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>
</body>
</html>
Conversion02Action-conversion.properties
Element_lists=org.person.Person
Key_maps=java.lang.String
Element_maps=org.person.Person
三、自定義類型轉換器
1.繼承DefaultTypeConverter
如果我們自定義一個類型轉換器,第一種方法是繼承DefaultTypeConverter,並重寫public Object convertValue(ActionContext context,Object value,Class toType);
這個函數的功能是完成雙向轉換,即從String數組轉到Action屬性及Action屬性轉到String;需要註意的是從String數組轉到Action屬性,而不是String轉到Action屬性;如果隻有一個字符串,則取params[0] 即可;
因此一般此函數的模板代碼如下:
public Object convertValue(Map<String, Object> context, Object value,
Class toType) {
if(toType==String.class){
……
return String;
}
else if(toType==Action屬性.class){
String params[] = (String[])value;//必須是字符串數組
…….
return Action屬性;
}
return null;
}
完成自定義類型轉換器編寫後,如果需要使用此轉換器,就需要配置,通常有兩種配置方法:
1.局部類型轉換文件中配置,僅對特定的Action的特定屬性有效,比如在Action1中有aa屬性,則僅對Action1的aa屬性有效;
2.全局類型轉換文件中配置,對某個類型都有效;比如對Person註冊瞭類型轉換器,則對任何Person和String的轉換都有效;
全局類型轉換文件命名為:xwork-conversion.properties,通常放在WEB-INF\classes下;
文件內容如:attributeName=ConvertClass ,比如aa=org.convert.Converter1
attributeName表示屬性名稱,convertClass表示轉換器的實現類;
局部類型轉換文件命名為:ActionName-conversion.properties,放在特定Action的目錄下;
文件內容如:typeName=ConvertClass,比如org.person.Person = org.convert.Converter2
註意:typeName表示轉換類型,convertClass表示轉換器的實現類;
代碼示例:
Conversion03Action.java
package org.conversion.action;
import org.person.Person;
import com.opensymphony.xwork2.ActionSupport;
public class Conversion03Action extends ActionSupport {
private Person p ;
public String execute()throws Exception{
System.out.println(p.getGender());
System.out.println(p.getSalary());
return SUCCESS;
}
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
}
Converter01.java
package org.converter;
import java.util.Map;
import org.person.Person;
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;
public class Converter01 extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value,
Class toType) {
if(toType==String.class){
Person p = (Person)value;
return p.getSalary()+","+p.getGender();
}
else if(toType==Person.class){
String params[] = (String[])value;
String[]values = params[0].split("\\,");
Person p = new Person();
p.setGender(values[1]);
p.setSalary(Double.parseDouble(values[0]));
return p;
}
return null;
}
}
3.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<s:fielderror></s:fielderror>
<s:form action="conversion03" >
<s:textfield label="薪水,性別" name="p"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
</body>
</html>
Conversion03Action-conversion.properties
p=org.convert.Converter01
2.繼承StrutsTypeConverter
StrutsTypeConverter是DefaultTypeConverter的子類,DefaultTypeConverter的類型轉換器是在一個函數中進行雙向轉換,而繼承StrutsTypeConverter的類型轉換器則是將兩個方向分別用兩個函數實現:
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
}
@Override
public String convertToString(Map context, Object o) {
}
將DefaultTypeConverter中的Converter01換成如下代碼就可以完成StrutsTypeConverter的類型轉換器;
package org.converter;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
import org.person.Person;
public class Converter02 extends StrutsTypeConverter {
@Override
public Object convertFromString(Map context, String[] value, Class toClass) {
String params[] = (String[])value;
String[]values = params[0].split("\\,");
Person p = new Person();
p.setGender(values[1]);
p.setSalary(Double.parseDouble(values[0]));
return p;
}
@Override
public String convertToString(Map context, Object o) {
Person p = (Person)o;
return p.getSalary()+","+p.getGender();
}
}
xwork-conversion.properties
orgorg.person.Person=org.converter.Converter02
四、錯誤處理機制
客戶輸入錯誤信息是很正常的事,因此需要對此種行為進行處理,Struts2提供瞭很好的錯誤處理機制,是由conversionError攔截器自動完成,我們隻需要配置輸出什麼錯誤信息即可;
當發生類型轉換錯誤時,conversionError攔截器攔截此錯誤,並封裝成fieldError,將此錯誤信息放入ActionContext中,並返回input邏輯視圖;當然在<action>元素中必須配一個類似於<result name="input">/1.jsp</result>的子元素,以應對類型轉換錯誤;
而為瞭顯示錯誤信息,則在jsp頁面中使用<s:fielderror/>即可顯示錯誤信息;
註意:類型轉換的Action在struts.xml中配置所在包需要extends="struts-default",因此在此文件中包含conversionError攔截器;
這時我們需要配置顯示的錯誤信息,錯誤信息配置方式有兩種:
1.配置默認提示信息
在國際化資源文件中配置形如:
xwork.default.invalid.fieldvalue={0},錯誤;
{0}代表類型轉換錯誤的屬性名稱;
2.配置Action特定屬性錯誤信息
在Action范圍的國際化資源文件中配置形如:
invalid.fieldvalue.attributeName= 內容
attributeName是Action中的屬性名稱;
我們在上面的程序中添加類型轉換錯誤的消息:
在Conversion04Action_zh_CN.properties中添加:
<span style="font-family:'Microsoft YaHei';">invalid.fieldvalue.p=person類型轉換錯誤;</span>
意思是對p屬性配置瞭特定的錯誤提示信息,當p屬性類型轉換錯誤後就會顯示此信息;
摘自xiazdong的專欄