struts2学习之---类型转换

所有的MVC框架,都需要负责解析HTTP请求参数,并将请求参数传给控制器组件,HTTP请求参数都是字符串类型,但Java是强类型的语言,因此MVC框架必须将这些字符串参数转换成相应的数据类型—这个工作时所有的MVC框架都应该提供的功能。
Struts2的类型转换可以基于OGNL表达式,只要把HTTP参数(表单元素和其他GET/POST的参数)命名为合法的OGNL表达式,就可以充分利用struts2的类型转换机制。

1.struts2内建的类型转换器
  • boolean和Boolean:完成字符串和布尔值之间的转换。
  • char和Character:完成字符串和字符之间的转换。
  • int和Integer:完成字符串和整型值之间的转换。
  • long和Long:完成字符串和长整型值之间的转换。
  • float和Float:完成字符串和单精度浮点值之间的转换。
  • double和Double:完成字符串和双精度浮点值之间的转换。
  • Date:完成字符串和日期类型之间的转换,日期格式使用用户请求所在Locale的SHORT格式。
  • 数组:在默认情况下,数组元素是字符串,如果用户提供了自定义类型转换器,也可以是其他复合类型的数组。
  • 集合:在默认情况下,假定集合元素类型时String,并创建一个新的ArrayList封装所有的字符串。
2.基于OGNL的类型转换

借助OGNL表达式的支持,struts2允许以另一种简单方式将请求参数转换成复合类型。

LoginAction.java

    public class LoginAction extends ActionSupport{
        private User user;
        set,get方法

        public String execute() throws Exception{
            if(getUser().getName().equals(" ") && getUser().getPass().equals(" ")){
                addActionMessage("转换成功");
                return SUCCESS;
            }
            addActionMessage("转换失败");
            return ERROR;
        } 
    }

login.jsp

<s:form action="login">
    <s:textfield name="user.name" label="用户名"/>
    <s:textfield name="user.pass" label="密码"/>
    <s:submit value="转换"/>
    struts2会把user.name参数的值赋值给Acion实例的user属性的name属性,user.pass参数同理。

</s:form>   

struts2将普通请求参数转换成复合类型对象时需要注意:

  • 因为struts2将通过反射来创建一个复合类(User类)的实例,因此系统必须为该复合类提供无参数的构造器。
  • 如果希望使用user.name请求参数的形式为Action实例的user属性的name属性赋值,则必须为user属性对应的复合类(User类)提供setName()方法,因为struts2是通过调用该方法来为该属性赋值的。当然Action类中还应该包含getUser()方法。
    public class LoginAction extends ActionSupport{
        private Map<String,User> users;
        set,get方法
        public String execute() throws Exception{
            if(getUsers().get("one").getName().equals(" ") && getUsers().get("one").getPass().equals(" ")){
                addActionMessage("登录成功");
                return SUCCESS;
            }
            addActionMessage("登录失败");
            return ERROR;
        }
    }

login.jsp
<s:form action="login">
    <s:textfield name="users['one'].name" label="第one个用户名"/>
    <s:textfield name="users['one'].pass" label="第one个密码"/>
    <s:textfield name="users['two'].name" label="第two个用户名"/>
    <s:textfield name="users['two'].pass" label="第two个密码"/>
    <s:submit value="登录">
</s:form>   

将表单域的name属性设置为”Action属性名[‘key值’].属性名”,其中”Action属性名”是Action类型包含的Map类型属性,后一个属性名则是Map对象里复合类型对象的属性名。

如果将LoginAction中user属性改为List<User>,一样可以利用OGNL表达式做到,只要通过索引来指定要将请求参数转换成List的哪个元素。

    <s:form action="login">
        <s:textfield name="user[0].name" label="第一个用户名"/>
        <s:textfield name="user[0].pass" label="第一个密码"/>
        <s:textfield name="user[1].name" label="第二个用户名"/>
        <s:textfield name="user[1].pass" label="第二个密码"/>
    </s:form>
3.指定集合元素的类型

前面使用集合时都使用了泛型,这种泛型可以让struts2了解集合元素的类型,struts2就可通过反射来创建对应类的对象,并将这些对象添加到List中。
如果不使用泛型,struts2允许开发者通过局部类型转换文件来指定集合元素的类型。类型转换文件就是一个普通的Properties(*.properties)文件。

    public class LoginAction extends ActionSupport{
        private List users;
        set,get方法
        public String execute() throws Exception{
            //因为没有使用泛型,所以要进行强制类型转换
            User firstUser = (User)getUsers().get(0);
            if(firstUser.getName().equals(" ") && firstUser.getPass.equals(" ")){
                addActionMessage("登录成功");
                return SUCCESS;
            }
            addActionMessage("登录失败");
            return ERROR;
        }
    }

局部类型转换文件的文件名为ActionName-conversion.properties类型转换文件应该放在和Action类文件相同的位置。
为了指定List集合里元素的数据类型,需要指定两个部分:
1. List集合属性的名称。
2. List集合里元素的类型。
通过在局部类型转换文件中指定key-value对:
Element_<ListPropNmae>=<ElementType>
ListPropName替换成List集合属性的名称、ElementType替换成集合元素的类型,如下:
Element_users=com.hyq.User
如果对于Map类型的属性,则需要同时指定Map的key类型和value类型。为了指定Map类型属性key属性,应该在局部类型转换文件中增加:
`Key_<MapPropName>=<KeyType>
Key是固定的,MapPropName是Map类型属性的属性名,复合类型指定的是Map的key值的全限定类型。
value类型:
Element_<MapPropName>=<ValueType>
Element是固定的,MapPropName是Map类型属性的属性名,复合类型指定的是Map的key值的全限定类型。

4.自定义类型转换器

需要把一个字符串转换成一个复合对象(例如User对象)时,这就需要使用自定义类型转换器。

    public class LoginAction extends ActionSupport{
        private User user;
        set,get方法
        if(getUser().getName().equals(" ") && getUser().getPass().equals(" ")){
                addActionMessage("转换成功");
                return SUCCESS;
            }
            addActionMessage("转换失败");
            return ERROR;
    }

struts2的类型转换器实际上依然是基于OGNL框架的,在OGNL项目中有一个TypeConverter接口,这个接口就是自定义类型转换器必须实现的接口。

    public interface TypeConverter{
        public Object convertValue(Map context,Object target,Member member,String propertyName,Object value,Class toType);
    }

实现类型转换器必须实现上面的TypeConverter,不过上面接口里的方法太复杂,所以OGNL项目还为该接口提供了一个实现类:DefaultTypeConverter,通常都采用扩展类来实现自定义类型转换器。实现自定义类型转换器需要重写DefaultTypeConverter类的converType()方法。

UserConverter.java

        public class UserConverter extends DefaultTypeConverter{
            public Object convertValue(Map context,Object value,Class toType){
                if(toType == User.class){
                    //系统的请求参数是一个字符串数组
                    String[] params = (String[]) value;
                    User user = new User();
                    //只处理请求参数数组第一个数组元素
                    //并将该字符串以英文逗号分割成两个字符串
                    String[] userValues = params[0].split(",");
                    user.setName(userValues[0]);
                    user.setPass(userValues[1]);
                    return user;
                }else if(toType == String.class){
                    User user = (User)value;
                    return "<" + user.getName() + "," + user.getPass + ">";
                }
                return null;
            }

        }
1. convertValue 方法的作用
该方法负责完成类型的转换,不过这种转换时双向的:当需要把字符串转换成User实例时,是通过该方法完成的;当需要把User实例转换成字符串时,也是通过该方法完成的。程序通过toType的类型即可判断转换的方向。
2. conterValue方法参数和返回值的意义
第一个参数:context是类型转换环境的上下文。
第二个参数:value是需要转换的参数,随着转换方向的不同,value参数的值也是不一样的。
第三个参数:toType是转换后的目标类型。
转换器的convertValue方法,接受到需要转换的值,需要转换的目标类型为参数,然后返回转换后的目标值。
3.当把字符串向User类型转换时,为什么value是一个字符串数组,而不是一个字符串。
因为一个下拉框可以选择多个值,这是下拉框对应的请求参数则是字符串。
对于DefaultTypeConverter转换器而言,它必须考虑到最通用的情形,因此将所有的请求参数都视为字符串数组。
可以认为DefaultTypeConverter是通过HttpServletRequest的getParameterValues(name)方法来获取请求参数值的。
5.注册类型转换器
  • 注册局部类型转换器:局部类型转换器仅仅对某个Action的属性起作用。
  • 注册全局类型转换器:全局类型转换器对所有Action的特定类型的属性都会生效。
  • 使用JDK1.5的注解来注册类型转换器:通过注解方式来注册类型转换器

    1. 局部类型转换器
      注册局部类型转换器使用局部类型转换文件指定,只要在局部类型转换文件中添加:
      <propName>=<ConverterClass>
      user=com.hyq.UserConverter
      propName替换成需要进行类型转换的属性、ConverterClass替换成类型转换器的实现类即可。
      局部类型转换器只对指定Action的特定属性起作用,有很大的局限性。通常会将类型转型器注册成全局类型转换器,让该类型转换器对该类型的所有属性起作用。
    2. 全局类型转换器
      全局类型转换器不是对指定Action的指定属性起作用,而是对指定类型起作用。
      注册全局类型转换器应该提供一个xwork-conversion.properties文件,该文件直接放在WEB-INF/classes路径下。
      com.hyq.vo.User = com.hyq.UserConverter
      该全局类型转换器就会对所有类型为com.hyq.vo.User类型的属性起作用。
    3. 局部类型转换器和全局类型转换器的说明
      局部类型转换器是对指定Action的指定属性进行转换,该转换器的转换方法对该属性只转换一次。
      全局类型转换器会对所有Action的特定类型进行转换,全局类型转换将不是对该集合属性整体进行转换,而是对该集合属性的每个元素进行转换。
6.基于struts2的自定义类型转换器

为了简化类型转换器的实现,struts2提供了一个StrutsTypeConverter抽象类,这个抽象类是DefaultTypeConverter类的子类。StrutsTypeConverter类简化了类型转换器的实现,该类已经实现了DefaultTypeConverter的convertValue()方法。当需要将字符串转换成复合类型时,调用convertFormString()抽象方法;当需要把复合类型转换成字符串时,调用convertToString()抽象方法。

    public class UserConverter extends StrutsTypeConverter{
        public Object convertFromString(Map context,String[] values,Class toClass){
            User user = new User();
            String[] userValues = values[0].split(",");
            user.setName(userValues[0]);
            user.setPass(userValues[1]);
            return user;
        }
        public String convertToString(Map context,Object o){
            User user = (User)o;
            return "<" + user.getName() + "," + user.getPass() + ">";

        }   

    }
7.处理Set集合

通常不建议在Action中使用Set集合属性,因为Set集合里元素处于无序状态。除非Set集合里的元素有一个标识属性,这个属性可以唯一地表示集合元素,这样struts2就可以根据该标识属性类存取集合元素。

    public class LoginAction extends ActionSupport{
        private Set users;
        set,get方法
    }
    public class UserConverter extends StrutsTypeConverter{
        public Object convertFromString(Map context,String[] values,Class toClass){
        Set result = new HashSet();
        for(int i = 0;i < values.length;i++){
            User user = new User();
            String[] userValues = values[0].split(",");
            user.setName(userValues[0]);
            user.setPass(userValues[1]);
            result.add(User);
        }
        return result;
        }
        public String convertToString(Map context,Object o){
            if(o.getClass() == Set.class){
                Set user = (Set)o;
                String result = "[";
                for(Object obj : users){
                    User user = (User)obj;
                    result += "<" + user.getName() + "," + user.getPass() + ">";
                }
                return result + "]";
            }else{
                return "";
            }

        }   
    }   

    public class User{
        private String name;
        private String pass;
        set,get方法
        public boolean equals(Object obj){
            if(this == obj){
                return true;
            }
            if(obj != null && obj.getClass() == User.class){
                User user = (User)obj;
                return this.getName().equals(user,getName());
            }
            return false;
        }
        public int hashCode(){
            return name.hashCode();
        }
    }

重写了equals和hashCode两个方法,可以看出,该User类的标识属性是name,当两个User的name相等时即可认为它们相等。
struts2通过局部类型转换文件来指定Set集合元素的标识属性,在局部类型转换文件中增加:

KeyProperty_<SetPropName>=<keyPropName>
users = com.hyq.UserConverter
KeyProperty_users=name

一旦指定了集合元素的索引属性后,struts2就可以通过该索引属性来存取Set集合元素:
<s:property value="users('huang').name"/>

8.类型转换中的错误处理

(1. )处理类型转换错误
为了让Struts2类型转换的错误处理机制生效,包括下一节的输入校验生效,都必须让Action继承Struts2的ActionSupport基类,因为ActionSupport负责收集类型转换错误,输入校验错误,并将它们封装成FieldError对象,添加到ActionContext中
对于中文环境,出入中文提示,需要在国际化资源文件中增加:
xwork.default.invalid.fieldvalue={0}字段类型转换失败!
上面的资源文件中包含了非西欧字符,因此必须使用native2ascii命令来处理该文件。
在某些时候,可能还需要对特定字段指定特别的提示信息,此时可通过Action的局部资源文件来实现,增加:

    invalid.fieldvalue.<propName>=<tipMsg>
    invalid.fieldvalue.user.birth=生日信息必须满足yyyy-MM-dd格式
  • propNmae替换成需要进行类型转换的属性名(此处支持OGNL表达式)
  • tipMsg替换转换失败后的提示信息。

(2.) 处理集合属性的转换错误

    public class LoginAction extends ActionSupport{
        private List<User> user;
        set,get方法
    }

上面Action中的users是一个List集合,此处有两种方式为users传入请求参数:

  • 只传入一个users请求参数,该请求参数的值是字符串数组的形式。
  • 分别传入多个user[0]、user[1]···形式的请求参数,这种形式将会充分利用OGNL表达式类型转换机制。
    对于第一种形式,因为只有一个请求参数,请求参数名为users,只要任何一个users请求参数不能成功转换成User对象,Struts2都会提示users字段无效。
    第二种形式:

     <s:form action="login">
         <s:iterator value="{0,1,2}" status="stat">
            <s:textfield name="users[%{#stat.index}]" label="第%{#stat.index}个用户信息">
         </s:iterator>
         <s:submit value="转换"/>
     </s:form>

    上面页面使用了迭代器标签来指定三个表单域的name,三个表单域的name将分别是users[0]、users[1]、users[2],在这种情况下如果任何一个表单域类型转换失败,都会出现提示字段。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值