类型转换是Struts 2的一个非常重要的部分,通过类型转换能够将表单参数转换成Java中的各种类型,本文将详细介绍Struts 2的内建类型转换器和自定义类型转换器。
第一节 Struts 2内建类型转换器
1.1 类型转换的意义
Web应用程序的交互是建立在HTTP之上的,互相传递的都是字符串,也就是说服务器接收到的来自用户的数据只能是字符串或者是字符数组,而在Web应用的对象中,往往使用了多种不同的类型,如整数(int)、浮点数(float)、日期(Date)或者是自定义数据类型等,因此在服务器端必须将字符串转换成合适的数据类型,服务器端完成业务逻辑处理后,又需将其他数据类型转换为String,然后传递给客户端进行显示,具体流程如图所示:
1.2 内建类型转换器简介
Struts 2为常用的数据类型提供了内建的类型转换器,Struts在遇到这些类型时,会自动去调用相应的转换器进行类型转换,Struts 2提供了如下一些内建的类型转换器:
A、基本数据类型以及其封装类
B、日期类型
C、集合(Collection)类型
D、集合(Set)类型
E、数组类型
下面几小节内容将对上述内建类型转换器进行简单介绍
1.3 基本数据类型转换器
通过基本数据类型转换器能够完成字符串和基础数据类型之间的转换,其中基础数据类型包括boolean、char、byte、short、int、long、float和double八种。
下面通过一个简单的计算器示例来演示如何通过基本数据类型转换器完成字符串和double类型数据之间的转换:
CounterInput.jsp:
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>计算器</title> </head> <body> <form action="count.action" method="post"> 第一个操作数:<input type="text" name="num1"><br><!--第一个操作数--> 运算符:<select name="operation"> <option value="add">+</option><!--加法运算符--> <option value="minus">-</option><!--减法运算符--> <option value="multiply">*</option><!--乘法运算符--> <option value="division">/</option><!--除法运算符--> </select><br> 第二个操作数:<input type="text" name="num2"><br><!--第二个操作数--> <input type="submit" value="运算"> <input type="reset" value="重置"> </form> </body> </html>
CounterOutput.jsp:
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>计算结果</title> </head> <body> <s:property value="num1"/> <s:property value="operation"/> <s:property value="num2"/>= <s:property value="sum"/> </body> </html>
CountAction.java:
package com.sanqing.action; import com.opensymphony.xwork2.ActionSupport; public class CountAction extends ActionSupport { private double num1; //第一个操作数 private double num2; //第二个操作数 private double sum; //运算结果 private String operation; //运算符 public double getNum1() { //获得第一个操作数 return num1; } public void setNum1(double num1) { //设置第一个操作数 this.num1 = num1; } public double getNum2() { //获得第二个操作数 return num2; } public void setNum2(double num2) { //设置第二个操作数 this.num2 = num2; } public String getOperation() { //获得运算符 return operation; } public void setOperation(String operation) {//设置运算符 this.operation = operation; } public double getSum() { //获得运算结果 return sum; } public void setSum(double sum) { //设置运算结果 this.sum = sum; } public String execute() throws Exception { if("add".equals(operation)) { //如果运算符为加法 sum = num1 + num2; }else if("minus".equals(operation)) { //如果运算符为减法 sum = num1 - num2; }else if("multiply".equals(operation)) {//如果运算符为乘法 sum = num1 * num2; }else { //如果运算符为除法 sum = num1 / num2; } return this.SUCCESS; } }
测试:访问http://localhost:8080/StrutsPro/CounterInput.jsp,其中计算器输入页面如下图所示:
注:在字符串类型和double数据类型之间进行转换时,如果输入一个非数字的数据,如“a”,这时将无法进行类型转换,程序将出现类型转换异常。
1.4 基本数据类型的封装类转换器
Struts 2的内置转换器不但能转换基本数据类型,还能够转换基本数据类型的封装类,包括Boolean、Character、Byte、Short、Integer、Long、Float和Double八种。
在下面这段代码中修改上述案例,其运行结果与上例相同:
package com.sanqing.action; import com.opensymphony.xwork2.ActionSupport; public class CountAction2 extends ActionSupport { private Double num1; //第一个操作数 private Double num2; //第二个操作数 private Double sum; //运算结果 private String operation; //运算符 public double getNum1() { //获得第一个操作数 return num1; } public void setNum1(Double num1) { //设置第一个操作数 this.num1 = num1; } public double getNum2() { //获得第二个操作数 return num2; } public void setNum2(Double num2) { //设置第二个操作数 this.num2 = num2; } public String getOperation() { //获得运算符 return operation; } public void setOperation(String operation) {//设置运算符 this.operation = operation; } public double getSum() { //获得运算结果 return sum; } public void setSum(Double sum) { //设置运算结果 this.sum = sum; } public String execute() throws Exception { if("add".equals(operation)) { //如果运算符为加法 sum = num1 + num2; }else if("minus".equals(operation)) { //如果运算符为减法 sum = num1 - num2; }else if("multiply".equals(operation)) {//如果运算符为乘法 sum = num1 * num2; }else { //如果运算符为除法 sum = num1 / num2; } return this.SUCCESS; } }
注:在JDK 5.0以后,Java提供了自动封包和解包(也称为自动装箱和拆箱),使得基本数据类型与其封装类之间可以互相自动转换
1.5 数组类型转换器
Struts 2还提供了一个数组类型的转换器,这个转换器非常常用,比如多个表单元素的name属性相同,那么提交的参数就不再是字符串而是一个字符串数组。通过Sturts 2提供的数组类型的转换器就能很方便的将多个相同name属性的表单元素的值封装到Action中的一个数组中。
下面这段代码主要演示Struts 2的数组类型转换器,将多个相同name属性的表单元素的值封装到Action中的一个数组中,需要注意的是表单元素的name必须与Action中的属性名称一致
(1)个人爱好输入页面LikeInput.jsp
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>个人爱好</title> </head> <body> <form action="mylike.action" method="post"> 请输入您的个人爱好: <br> <input type="checkbox" name="mylike" value="basketball">篮球 <input type="checkbox" name="mylike" value="football">足球 <input type="checkbox" name="mylike" value="pingpang">乒乓球 <input type="checkbox" name="mylike" value="billiards">台球<br> <input type="submit" value="提交"> <input type="reset" value="重置"> </form> </body> </html>
(2)个人爱好输出页面LikeOutput.jsp
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>显示个人爱好</title> </head> <body> 你输入的个人爱好包括: <s:property value="mylike"/> </body> </html>
(3)
package com.sanqing.action; import com.opensymphony.xwork2.ActionSupport; public class MyLikeAction extends ActionSupport{ private String[] mylike; //数组类型用来封装所有的个人爱好 public String[] getMylike() { //获得数组 return mylike; } public void setMylike(String[] mylike) {//设置数组 this.mylike = mylike; } public String execute() throws Exception { return this.SUCCESS; //返回success字符串 } }
(4)测试
1.6 集合类型转换器
集合类型转换器和数组类型转换器类似,可以通过集合类型来接受参数值。Struts 2提供的集合类型转换器,会将参数值自动添加到集合类型属性中,不同的是将集合类型转换成字符串类型时,输出了一对中括号
package com.sanqing.action; import java.util.ArrayList; import com.opensymphony.xwork2.ActionSupport; public class MyLikeAction2 extends ActionSupport{ private ArrayList<String> mylike; //ArrayList类型用来封装所有的个人爱好 public ArrayList<String> getMylike() { //获得mylike属性值 return mylike; } public void setMylike(ArrayList<String> mylike) {//设置mylike属性值 this.mylike = mylike; } public String execute() throws Exception { return this.SUCCESS; //返回success字符串 } }
1.7 日期类型转换器
通过日期类型转换器能够自动完成字符串类型与日期类型之间的转换,不过字符串类型必须符合日期格式规范,否则将出现类型转换错误。
下面这段代码主要演示Struts 2的日期类型转换器,通过输入用户出生日期,提交后自动输出用户年龄
(1)出生日期输入页面DateInput.jsp
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>输入出生日期</title> </head> <body> <form action="date.action" method="post"> 请输入您的出生日期: <br> <input type="text" name="mybirth"> <input type="submit" value="提交"> <input type="reset" value="重置"> </form> </body> </html>
(2)出生日期输出页面DateOutput.jsp
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>当前年龄</title> </head> <body> 经计算,年龄为<s:property value="age"/> </body> </html>
(3)Action
package com.sanqing.action; import java.util.Date; import com.opensymphony.xwork2.ActionSupport; public class DateAction extends ActionSupport{ private Date mybirth; //出生日期 private Long age; //年龄 public Date getMybirth() { //获得出生日期 return mybirth; } public void setMybirth(Date mybirth) { //设置出生日期 this.mybirth = mybirth; } public Long getAge() { //获得年龄信息 return age; } public void setAge(Long age) { //设置年龄信息 this.age = age; } public String execute() throws Exception { Date now = new Date(); //获得当前日期 long nowTime = now.getTime(); //获得当前日期毫秒数 long birthTime = mybirth.getTime(); //获得生日的毫秒数 long difference = nowTime - birthTime; //获得毫秒差 long differenceDay = difference/(1000*60*60*24);//计算出总天数 age = differenceDay/365+1; //计算出年数差加一 return this.SUCCESS; //返回success字符串 } }
注:在测试上述代码时,输入出生日期时必须严格按照日期格式进行输入,日期格式为yyyy-MM-dd,其中yyyy为年份,如1998;MM为月份,如12;dd为该月的某一天,如03。
第二节 自定义类型转换器
Struts 2已经实现了一些常用的类型转换,但是毕竟这些类型转换器还是有限的,如果是开发者自己定义的数据类型,就必须自定义类型转换器来进行转换,Struts 2的类型转换器是基于OGNL实现的,在lib目录下的ognl-2.6.11.jar中可以找到TypeConverter类,此处略去其详细内容。
下面来看如何创建自定义的类型转换器,假如现在有一个自定义的类Point类,包括x和y两个属性,分别代表x坐标值和y坐标值。那么如果通过表单的参数值来设置Point类的属性呢,这时就必须通过自定义类型转换器来完成。
2.1 实现自定义类型转换器
package com.sanqing.converter; import java.util.Map; import ognl.DefaultTypeConverter; import com.sanqing.bean.Point; public class PointConverter extends DefaultTypeConverter { public Object convertValue(Map context, Object value, Class toType) { if(Point.class == toType) {//首先判断如果要转换的类型是否为Point String[] paramStrs = (String[])value;//将请求参数值转换成String数组 String[] pointArray = paramStrs[0].split(",");//通过逗号分隔字符串 Point point = new Point();//新建一个Point实例 point.setX(Integer.parseInt(pointArray[0]));//设置x坐标 point.setY(Integer.parseInt(pointArray[1]));//设置y坐标 return point;//返回转换后的Point实例 } if(String.class == toType) {//如果要转换的类型为String Point point = (Point)value;//通过强制转换把value转换成Point实例 int x = point.getX();//获得x坐标 int y = point.getY();//获得y坐标 StringBuilder sb = new StringBuilder();//实例化一个StringBuilder用来构造字符串 sb.append("x坐标为").append(x).append(",y坐标为").append("y");//构造字符串 return sb.toString();//返回StringBuilder的字符串形式 } return null; } }
2.2 使用Struts 2提供的StrutsTypeConverter
package com.sanqing.converter; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; import com.sanqing.bean.Point; public class PointConverter2 extends StrutsTypeConverter{ //从字符串类型转换成自定义数据类型 public Object convertFromString(Map context, String[] values, Class toClass) { String[] pointArray = values[0].split(",");//通过逗号分隔字符串 Point point = new Point();//新建一个Point实例 point.setX(Integer.parseInt(pointArray[0]));//设置x坐标 point.setY(Integer.parseInt(pointArray[1]));//设置y坐标 return point;//返回转换后的Point实例 } //从自定义数据类型转换成字符串类型 public String convertToString(Map context, Object o) { Point point = (Point)o;//通过强制转换把o转换成Point实例 int x = point.getX();//获得x坐标 int y = point.getY();//获得y坐标 StringBuilder sb = new StringBuilder();//实例化一个StringBuilder用来构造字符串 sb.append("x坐标为").append(x).append(",y坐标为").append("y");//构造字符串 return sb.toString();//返回StringBuilder的字符串形式 } }
2.3 注册局部类型转换器
(1)PointInput.jsp
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>输入坐标</title> </head> <body> <form action="point.action" method="post"> 请输入点坐标(如:12,13): <br> <s:fielderror name="point"/> <input type="text" name="point"><br> <input type="submit" value="提交"> <input type="reset" value="重置"> </form> </body> </html>
(2)PointOutput.jsp
<%@page language="java" pageEncoding="gb2312"%> <%@taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>输出坐标</title> </head> <body> <s:property value="point"/> </body> </html>
(3)
package com.sanqing.action; import com.opensymphony.xwork2.ActionSupport; import com.sanqing.bean.Point; public class PointAction extends ActionSupport { private Point point; //point属性 public Point getPoint() { //获得point属性值 return point; } public void setPoint(Point point) { //设置point属性值 this.point = point; } public String execute() throws Exception { return SUCCESS; } }
2.4 注册全局类型转换器
2.5 类型转换执行流程
2.6 类型转换中错误处理
2.7 输出指定的错误信息
如果类型转换出错,Struts 2将自动调用ConversionError拦截器将错误提示信息保存到FieldError中