为什么会有类型转换? HTTP协议中传递的任何内容都是String类型的,所以一旦我们在服务器上需要一个非String类型的对象,例如:int或者Date,那么我们就需要在收到 HTTP请求的数据的时候,首先将String类型的数据变换为我们需要的对应类型的数据,之后再使用。这个过程就是类型转换 类型转换在Struts2中是透明的,即Struts2内置了类型转换机制。 转换原理: 以一个例子来说明如何使用Struts2内置的类型转换功能。加入我们希望用户在画面上输入一个字符形式的坐标点,例如(33,2)而我们希望在程序中得到一个Point(33, 2)的类型与之对应。 要想达到上面的功能我们需要一个名字位:ActionName-conversion.properties的文件,在文件中定义Action中的属性和画面字段之间的转换关系。例如: point = com.jpleasure.convertor.PointConverter 也就是说画面一个叫做point的项目(input类型,name为point)提交到服务器上之后,在向Action中的point属性赋值之前需要使用 PointConverter将字符串转换为Point类,在Action中的point属性向画面显示的时候需要使用PointConverter将 Point类转换为字符串类型。 其中PointConverter需要实现ognl.TypeConverter接口。TypeConverter有两个接口,一个负责将字符串转变为对象类型,另一个负责将对象类型转换为字符串类型,分别对应着内容的提交和显示。 有些时候我们希望所有的Point类在默认的情况下使用PointConverter来转换,这时候我们需要定义全局的Converter类。这可以在xwork-conversion.properties文件中定义,例如: com.jpleasure.Point = com.jpleasure.convertor.PointConverter 在Struts2中提供了一个TypeConverter接口的默认实现: org.apache.struts2.action.util.StrutsTypeConverter 这个类有两个默认的抽象转换方法和performFallbackConversion,performFallbackConversion方法负责处理类型转换出错的处理。 在自定义TypeConverter的时候,可以实现TypeConverter接口,之后编写TypeConverter的转换方法,也可以从 StrutsTypeConverter继承而来,StrutsTypeConverter本身实现了TypeConverter接口,并且实现了基本的转换方法。 内建的转换: Struts2内建了对以下类型的转换的支持: String boolean / Boolean char / Character int / Integer, float / Float, long / Long, double / Double dates - 使用HTTP 请求对应地域(Locale)的SHORT形式转换字符串和日期类型。 arrays -每一个字符串内容可以被转换为不同的对象 collections - 转换为Collection类型,默认为ArrayList类型,其中包含String类型。 对于Array类型和Collection类型,需要对其中的每一个元素进行单独的转换。 自定义TypeConverter: 使用如下的代码自定义需要的TypeConverter public class MyConverter extends StrutsTypeConverter { public Object convertFromString(Map context, String[] values, Class toClass) { ..... } public String convertToString(Map context, Object o) { ..... } } 为了让Struts2框架发现类型转换的错误,需要在出错的情况下在上述的两个方法中抛出XWorkException或者TypeConversionException。 我们使用一个例子来展现如何实现TypeConvertor类型: // Point 类 package com.jpleasure.conversion; public class Point { private int x; private int y; public Point() { } public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public String toString() { StringBuffer sb = new StringBuffer("Point("); sb.append(x).append(", ").append(y).append(")"); return sb.toString(); } } // PointConvertor 类 package com.jpleasure.conversion; import org.apache.struts2.util.StrutsTypeConverter; import java.util.Map; public class PointConvertor extends StrutsTypeConverter { // 从字符串转换为对象的方法。 public Object convertFromString(Map map, String[] strings, Class aClass) { if (strings.length > 0) { String pointStr = strings[0]; String[] pointStrArray = pointStr.split(","); if (pointStrArray.length == 2) { Point p = new Point(); p.setX(Integer.parseInt(pointStrArray[0])); p.setY(Integer.parseInt(pointStrArray[1])); return p; } else { return null; } } else { return null; } } // 从对象转换为字符串的方法。 public String convertToString(Map map, Object o) { if (o instanceof Point) { return o.toString(); } else { return ""; } } } // 测试用PointAction类 package com.jpleasure.action; import com.jpleasure.conversion.Point; import com.opensymphony.xwork2.ActionSupport; public class PointAction extends ActionSupport { private Point point; public Point getPoint() { return point; } public void setPoint(Point point) { this.point = point; } public String execute() { return SUCCESS; } } // JSP文件 Point Page // PointAction配置文件类。 /point/Point.jsp/point/Point.jsp 复杂的类型转换: (1)处理Null值 有些时候我们会被NullPointerException搞的焦头烂额,为什么系统不能为我们定义了但是没有初始化的对象建立一个空的Object的引用呢?Struts2有这个功能,但是在默认情况下Struts2关闭了这个功能,要想开启这个功能,需要在ParameterInterceptor开始处理参数之前在ValueStack中将一个值开启,这个值是:InstantiatingNullHandler.CREATE_NULL_OBJECTS。 在Java代码中InstantiatingNullHandler.CREATE_NULL_OBJECTS的值是:xwork.NullHandler.createNullObjects 创建空值对象的规则为: * 如果属性声明为Collection或List, 将返回一个ArrayList并赋值给空引用. * 如果属性声明为Map, 将返回一个HashMap并赋值给空引用. * 如果空值属性是一个带有无参构造函数的简单Bean, 将使用ObjectFactory.buildBean(java.lang.Class, java.util.Map)方法创建一个实例. (2)Collection和Map 简单List转换 //JSP代码 Point Page //Action代码 package com.jpleasure.action; import com.jpleasure.conversion.Point; import com.opensymphony.xwork2.ActionSupport; import java.util.List; public class PointAction extends ActionSupport { private Point point; private List references; public Point getPoint() { return point; } public void setPoint(Point point) { this.point = point; } public List getReferences() { return references; } public void setReferences(List references) { this.references = references; } public String execute() { if(references == null) { System.out.println("references is null"); } else { System.out.println("references length is:" + references.size()); for(Object s: references) { System.out.println("" + s); } } return SUCCESS; } } 对象类型List转换(key-value pair 方式) // Person 类型 package org.apache.struts2.showcase.conversion; import java.io.Serializable; public class Person implements Serializable { private String name; private Integer age; public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setAge(Integer age) { this.age = age; } public Integer getAge() { return this.age; } } // PersionAction 类型 package org.apache.struts2.showcase.conversion; import java.util.List; import com.opensymphony.xwork2.ActionSupport; public class PersonAction extends ActionSupport { private List persons; public List getPersons() { return persons; } public void setPersons(List persons) { this.persons = persons; } public String input() throws Exception { return SUCCESS; } public String submit() throws Exception { return SUCCESS; } } // PersonAction转化配置文件PersonAction-conversion.properties # PersonAction中persons属性(List类型)中元素的类型 Element_persons=org.apache.struts2.showcase.conversion.Person // JSP部分代码 其中stat记录了当前循环的信息,其中stat.index表示当前循环的下标。 所以上述代码会生成如下的代码: 对象类型List转换(value 方式) // Address 类型 package org.apache.struts2.showcase.conversion; public class Address { private String id; private String address; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } // AddressAction 类型 package org.apache.struts2.showcase.conversion; import java.util.LinkedHashSet; import java.util.Set; import com.opensymphony.xwork2.ActionSupport; public class AddressAction extends ActionSupport { private Set addresses = new LinkedHashSet(); public Set getAddresses() { return addresses; } public void setAddresses(Set addresses) { this.addresses = addresses; } public String input() throws Exception { return SUCCESS; } public String submit() throws Exception { System.out.println(addresses); return SUCCESS; } } //AddressAction转换配置文件AddressAction-conversion.properties KeyProperty_addresses=id Element_addresses=org.apache.struts2.showcase.conversion.Address CreateIfNull_addresses=true // JSP代码 上述代码会转换为: <s:form action="submitAddressInfo" namespace="/conversion"> <s:textfield label="Address 0" name="addresses('id0')" /> <s:textfield label="Address 1" name="addresses('id1')" /> <s:textfield label="Address 2" name="addresses('id2')" /> <s:submit /> </s:form> ------------------------------------------------------------------------------------------ 基本类型可以完成自动转换,主要转换对象类型,基于OGNL 分为: ----局部类型转换 1.自定义转换类继承DeaultTypeConverter 重写convertValue(Map context, Object value, Class toType) Xxx.class == toType (或用equals()) String --> Object String.class == toType Object --> String 2.或继承StrutsTypeConverter 重写 Object convertFromString(Map context,String[] values,Class toClass) String convertToString(Map context, Object o) 3.配置文件: Action类名-conversion.properties 需要和对应Action在同一目录下 内容: 属性名=转换类名 (多个属性就写多行) ----全局类型转换 1. 2. 同局部转换 3.配置文件: xwork-conversion.properties(固定的) 内容: 需要类型转换的Action的全称类名=转换类名 【局部类型转换】 ======================================= 1. input.jsp <h1>用逗号将点的两个坐标分割开</h1> 如:20,30 <s:form action="pointConverter"> <s:textfield name="point" label="点" /> <s:textfield name="age" label="年龄" /> <s:textfield name="username" label="用户名" /> <s:textfield name="date" label="生日" /> <s:submit label="提交" /> </s:form> 对于一个属性来说,先进行类型转化,转化成功了再进行输入验证 ======================================= 2.建立 模型类 .....test.bean 包 public class Point { private int x; private int y; //setter...getter...方法 } ======================================= 3.简要说明 用于实现转换的TypeCenverter接口: 主要使用DefaultTypeCenverter类 重写convertValue()方法 public Object convertValue(Map context, Object value, Class toType) ======================================= 4.建立自定义转换类 建立....test.converter包 建立 转换类 需要继承DeaultTypeConverter,DeaultTypeConverter实现了TypeConverter转换接口 public class PointConverter extends DeaultTypeConverter { @Override public Object convertValue( Map context, //上下文 Object value, //要转换的值(String数组) Class toType //目标类型(转换成什么) ) { //如果要转换的类型是Point if( Point.class == toType ) { Point point = new Point(); String[] str = (String[])value; //此处数组只有一个值,在0的位置上 String[] paramValues = str[0].split(","); int x = Integer.parseInt(paramValues[0]); int y = Integer.parseInt(paramValues[1]); point.setX(x); point.setY(y); return point; } //如果要转换的类型为String (也可以用系统自动转换) if( String.class == toType ) { Point point = (Point)value; int x = point.getX(); int y = poitn.getY(); String result = "[x=" + x + " , y=" + y + "]"; return result; } return null; } 注:为什么value是数组,因为页面的字段可以有同样的名字,如: <s:textfield name="username" /> <s:textfield name="username" /> 这样的话,获得username时就应该是多个,所以是字符串数组 ======================================= 5.建立action public class PointAction extends ActionSupport { private Point point; private int age; private String username; private Date date; //setter...getter... @Override public String execute() throws Execption { return SUCCESS; } } -------------------- 补充: Action接口中: Field Summary 字段摘要 ERROR 执行失败 INPUT 验证没有成功 LOGIN 没有执行成功,因为没有登陆 NONE 执行成功,但不相识任何题图 SUCCESS 执行成功 ======================================= 6. struts.xml <package name="struts2" extends="struts-default"> <action name="pointConverter" class="com.test.action.PointAction"> <result name="success">/output.jsp</result> </action> </package> ======================================= 7.output.jsp <%@ taglib prefix="s" uri="/strurs-tags" %> property标签自动调用value对应的字段的get方法,value="point"和getPoint()方法匹配 point: <s:property value="point"/><br/> age:<s:property value="age"/><br/> username:<s:property value="username"/><br/> date:<s:property value="date"/><br/> ${point }<br/> ${point.x }<br/> ${point.y } ======================================= 8.定义属性文件--用于指定转换类 该文件必须和对应的转换类在同一个包下 PointAction-conversion.properties PointAction: 对应哪个Action中的属性进行转换 -conversion.properties: 是固定不变的 PointAction-conversion.properties的内容 对那个属性进行转换=用那个类对其进行转换,多个属性就写多行 point=com....converter.PointConverter 【完成】 ======================================= 整体流程 1.提交请求->到struts.xml中去找对应的action,这里是pointConverter,找到之后,知道了有指定的类(PointAction)来处理请求 2.生成PointAction的实例,到PointAction中,把请求中的值调用set方法赋给该类的每个属性 3.当调用set方法前,首先检查,对于这个属性有没有自定义的类型转换,没有的时候就按照系统的行为进行默认的转换; 如果发现已经定义好了类型转换(检查PiontAction的目录下有没有PointAction-onversion.properties存在),然后到该.properties文件里找,你到底要通过哪个类转换哪一个属性。 4.到相应的转换类中,这里是PointConverter,然后判断转换的方向, Poitn.class==toType:通过字符串数组 转换到 Point对象,进入到相应的if流程 return后,流程回到PointAction中的setPoint方法,然后使用转换后的对象赋值给属性 5.将所有的属性赋值成功后,执行execute方法,然后通过返回值找到struts.xml中的action中的result对应的页面(output.jsp),显示结果。 6.然后发现<s:property>,每找到value,就到PointAction中调用相应的get方法,查找该字段有没有配置自动转换类,对于没有字段,应用系统默认的转换后直接返回。对于配置的,流程同上,到PointConverter中执行对应的if流程(String.class==toType),return后回到PointAction中的get方法返回其转换后的值,显示到页面。 【全局类型转换】 ======================================= 需求:3个点的坐标 ======================================= 多个属性的局部类型转换 input.jsp中 添加两个点的坐标 <s:textfield name="point2" label="点2" /> <s:textfield name="point3" label="点3" /> PointAction中 添加两个点字段 private Point point2; private Point point3; //setter...getter... PointAction-conversion.properties中 添加 point2=com........converter.PointConverter point3=com........converter.PointConverter result.jsp中 添加 点2:<s:property value="point2" /><br/> 点3:<s:property value="point3" /><br/> ======================================= 需求:要转换的对象分布在多个action里面,上面的方式比较麻烦 应使用全局的类型转换:对系统里所有满足要求的进行转换 ======================================= 配置文件: 配置文件名:xwork-conversion.properties(固定的) 应在classes目录下,也就是struts.xml相同的目录 放在src下就可以了 内容: 要转化那个类的全称类名=使用哪个类进行转换 com.scorpio.jh.struts2.test.bean.Point=com.scorpio.jh.struts2.test.converter.PointConverter 用PointConverter类对系统中所有的需要转换的类进行转换 ======================================= 通过继承util包下的StrutsTypeConverter类进行转换 ======================================= 1. 建立一个新的转换类,继承StrutsTypeConverter public class PointConverter2 extends StrutsTypeConverter { //从String转换到目标对象 @Override public Object convertFromString( Map context, String[] values, Class toClass) { Point point = new Point(); String[] paramValues = values[0].split(","); int x = Integer.parseInt(paramValues[0]); int y = Integer.parseInt(paramValues[1]); point.setX(x); point.setY(y); return point; } //从对象转换到String @Override public String convertToString( Map context, Object o ) { Point point = (Point)o; int x = point.getX(); int y = point.getY(); String result = "[x=" + x + ", y=" + "]"; return result; } } ======================================= 2.修改配置文件 xwork-conversion.properties com.scorpio.jh.struts2.test.bean.Point=com.scorpio.jh.struts2.test.converter.PointConverter2 【完成】 拓展: ======================================= 需求:Action中用一个集合类型,存储Piont对象 ======================================= 1.action类 public class PointAction extends ActionSupport { private List<Piont> point; private int age; private String username; private Date date; //setter...getter... public String execute() throws Exception { ... } } ======================================= 2.input.jsp <s:form action="pointConverter"> <s:textfield name="point" label="点1" /> name相同,会返回一个数组 <s:textfield name="point" label="点2" /> <s:textfield name="point" label="点3" /> <s:textfield name="age" label="年龄" /> <s:textfield name="username" label="用户名" /> <s:textfield name="date" label="生日" /> <s:submit label="提交" /> </s:form> ======================================= 3. result.jsp point: <s:property value="point"/><br/> age:<s:property value="age"/><br/> username:<s:property value="username"/><br/> date:<s:property value="date"/><br/> ======================================= 4.转换类 PointConverter3 public class PointConverter3 extends StrutsTypeConverter { //从String到对象 @Override public Object convertFromString( Map context, String[] values, Class toClass) { List<Point> list = new ArrayList<Point>(); for( String value : values ) { Point point = new Point(); String[] paramValues = value.split.split(","); int x = Integer.parseInt(paramValues[0]); int y = Integer.parseInt(paramValues[1]); point.setX(x); point.setY(y); list.add(point); } return list; } //从对象到String @Override public String convertToString( Map context, Object o ) { List<Point> list = (List<Point>)o; //StringBuilder 非同步版本的StringBuffer StringBuilder sb = new StringBuilder(); sb.append("["); int number = 0; for( Point point : list ) { ++number; int x = point.getX(); int y = point.getY(); sb.append(number).append(" x=").append(x).append(", y=").append(y).append(" "); } sb.append("]"); return sb.toString(); } } ======================================= 5.采用局部转换 PointAction-conversion.properties point=com....converter.PointConverter3 【完成】 ======================================= 注: 如果需要转换的字段没有使用泛型 List<Point> 则要在局部类型转换中加入 Element_ 头 + 你要转换的东西 但是建议使用泛型 ======================================= 补充: 可以用 属性名.属性的值 这样一种方式赋值 <s:form action="pointConverter"> <s:textfield name="point.x" label="x坐标" /> ********* <s:textfield name="point.y" label="y坐标" /> ********* <s:textfield name="age" label="年龄" /> <s:textfield name="username" label="用户名" /> <s:textfield name="date" label="生日" /> <s:submit label="提交" /> </s:form> ------------------------ public class PointAction extends ActionSupport { private Piont point; private int age; private String username; private Date date; //setter...getter... public String execute() throws Exception { ... } } ------------------------ 将全局和局部类型转换注释掉 用#号 ------------------------ 重写Point的toString() pubic String toString() { return "[x=" + x + ", y=" + y + "]"; }