首先,何為struts2的類型轉換器?
類型轉換器的作用是將請求中的字符串或字符串數組參數與action中的對象進行相互轉換。
一、大部分時候,使用struts2提供的類型轉換器以及OGNL類型轉換機制即可滿足大部分類型轉換需求。如:
類User.java
package models; public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
類LoginAction
package actions; import java.util.List; import models.User; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String execute() throws Exception { if (getUser().getUsername().equals("yangys") && getUser().getPassword().equals("123")) { return SUCCESS; } return ERROR; } }
login.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" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title><s:text name="login page"></s:text></title> </head> <body> <s:form action="login"> <s:textfield name="user.username" key="username" /> <s:password name="user.password" key="password" /> <s:submit value="login" /> </s:form> </body> </html>
不用做任何處理,表單中的user.username和user.password即可映射到LoginAction中的user對象上。
注:需提供相關的getter與setter
二、在特殊情況下,這種類型轉換滿足不了需求,比如需要把一個復雜字符串轉換為一個對象。
如用戶輸入"huaihaizi,123"需要將huaihaizi映射到username,把123映射到password。則需要提供自定義類型轉換器並將其注冊到struts2中,供系統調用並完成類型轉換。
為了模擬此需求,將login.jsp改為如下,通過一個user輸入框輸入用戶名密碼,以逗號分隔。
<%@ 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" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title><s:text name="login page"></s:text></title> </head> <body> <s:form action="login"> <s:textfield name="user" key="user" /> <s:submit value="login" /> </s:form> </body> </html>
User.java與LoginAction.java保持不變。此時為了達到user輸入框中的內容映射到user對象的username和password上,需要編寫一個自定義轉換器類。在OGNL項目中有一個TypeConvert接口,這個借口就是自定義類型轉換器必須實現的接口,該接口定義如下。
public interface TypeConverter { public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType); }
實現自定義類型轉換器必須實現上面的接口,但是該接口過於復雜,所以OGNL提供了實現類DefaultTypeConverter,通常都采用繼承並重寫DefaultTypeConverter的convertValue()方法來實現自定義類型轉換器,如上需求,需要編寫UserConverter.java,代碼如下:
package converters; import java.util.Map; import models.User; import ognl.DefaultTypeConverter; public class UserConverter extends DefaultTypeConverter { @Override public Object convertValue(Map context, Object value, Class toType) { if (toType == User.class) { String[] params = (String[]) value; User user = new User(); user.setUsername(params[0].split(",")[0]); user.setPassword(params[0].split(",")[1]); return user; } else if (toType == String.class) { User user = (User) value; String userString = "<" + user.getUsername() + "," + user.getPassword() + ">"; return userString; } return null; } }
這里,convertValue方法就是執行類型轉換邏輯的,參數value是轉換前的值,toType是轉換目標類型,通過判斷toType來執行轉換方向的邏輯代碼。如此例中,toType==User.class時,即為將頁面字符串轉換成User類的對象,由於適應頁面控件參數的通用性,頁面參數統一包裝成了字符串數組,如果是一個字符串,則為長度1的字符串數組。比如此例中,輸入為"huaihaizi,123",則把huaihaizi賦值給user對象的username,把123賦值給user對象的password,並將此user返回即可。反之則把user對象的username和password拼接成字符串返回。
三、struts2提供了一個StrutsTypeConverter抽象類,這個類是DefaultTypeConverter的子類,將convertValue的兩個轉換方向拆分成了兩個方法,
convertFromString(Map context,String[] values,Class toClass)
convertToString(Map context ,Object o)
邏輯更為清楚,用法與DefaultTypeConverter一致。
三、然后通過在struts2項目中注冊此自定義類型轉換器即可,注冊此自定義類型轉換器有三種方式。
1.注冊局部類型轉換器,局部類型轉換器僅僅對某個Action的屬性起作用
局部類型轉換器則是在該Action同一目錄下添加ActionName-convertion.properties,並在內部添加一行映射關系<propName>=<ConverterClass>。本例子中在LgoinAction.java所在包下添加LoginAction-convertion.properties文件,並在文件中添加user=converters.UserConverter即可。
2.注冊全局類型轉換器,全局類型轉換器對所有Action的特定類型的屬性都會生效
在源代碼根路徑下提供xwork-convertion.properties文件。並在文件中添加<propType>=<ConverterClass>。本例子中在src目錄下添加xwork-convertion.properties文件,並在文件中添加models.User=converters.UserConverter即可。
3.使用JDK1.5的注解來注冊類型轉換器。
局部類型轉換器與全局類型轉換器的區別:局部類型轉換器只針對局部變量進行一次性轉換,比如該局部變量是個List<User>,也是在局部類型轉換器中對該變量進行一次轉換。如果用全局類型轉換器,則該List中的每一個User都將進行一次轉換。
四、處理Set集合屬性的類型轉換,一般情況下不建議在Action中使用Set集合屬性,因為Set集合里元素是無序的,所以Struts2不能准確的將參數轉換成Set集合里的元素,也不能准確的讀取Set集合里的元素。除非Set集合的每個元素都有一個唯一標示,比如對於上面的User類來講,將username做為標識,則在Action的Set<User>中不能存在兩個User對象的username相同,需要重寫User的equals和hashCode方法。
@Override public int hashCode() { // TODO Auto-generated method stub return getUsername().hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj != null && obj.getClass() == User.class) { User objUser = (User) obj; if (objUser.getUsername().equals(this.getUsername())) { return true; } } return false; }
然后在局部類型轉換器注冊文件中指定該Set集合元素的標識。如該Set集合為users,則上面講過在LoginAction-conversion.properties中添加users=converters.UserConverter 即可注冊該局部類型轉換器,現在此行下面添加KeyProperty_users=username則可以制定users變量的唯一標識屬性是username。