zoukankan      html  css  js  c++  java
  • Struts2学习笔记(七)——类型转换

    1、自动类型转换

    Struts2内部提供大量类型转换器,用来完成数据类型转换问题:

    • String和boolean、Boolean:完成字符串与布尔值之间的转换
    • String和char、Character:往常字符串与字符之间的转换
    • String和int、Integer:完成字符串与整型之间的转换
    • String和long、Long:完成字符串与长整型值之间的转换
    • String和double、Double:完成字符串与双精度浮点值的转换
    • String和float、Float:完成字符串和单精度浮点之间的转换
    • String和Date:完成字符串和日期类型之间的转换,可以接收yyyy-MM-dd格式字符串
    • String和数组:可以将多个同名参数,转换到数组中
    • String和Map、List:支持将数据保存到List或者Map集合

    自动类型转换例子:

    表单信息:

    1 <form action="${pageContext.servletContext.contextPath}/converterAction.action">
    2     name:<input type="text" name="name"><br>
    3     password:<input type="password" name="password"><br>
    4     age:<input type="text" name="age"><br>
    5     birthday:<input type="text" name="birthday"><br>
    6     hobby:<input name="hobby" type="checkbox" value="music">music
    7         <input name="hobby" type="checkbox" value="movie">movie<br>
    8     <input type="submit" value="提交">
    9 </form>

    Action类:

     1 public class ConverterAction extends ActionSupport {
     2     private String name;
     3     private String password;
     4     private int age;
     5     private Date birthday;
     6     private String[] hobby;
     7     
     8     public String getName() {
     9         return name;
    10     }
    11     public void setName(String name) {
    12         this.name = name;
    13     }
    14     public String getPassword() {
    15         return password;
    16     }
    17     public void setPassword(String password) {
    18         this.password = password;
    19     }
    20     public int getAge() {
    21         return age;
    22     }
    23     public void setAge(int age) {
    24         this.age = age;
    25     }
    26     public Date getBirthday() {
    27         return birthday;
    28     }
    29     public void setBirthday(Date birthday) {
    30         this.birthday = birthday;
    31     }
    32     public String[] getHobby() {
    33         return hobby;
    34     }
    35     public void setHobby(String[] hobby) {
    36         this.hobby = hobby;
    37     }
    38     
    39     @Override
    40     public String execute() throws Exception {
    41         System.out.println("name: " + name);
    42         System.out.println("password: " + password);
    43         System.out.println("age: " + age);
    44         System.out.println("birthday: " + birthday);
    45         System.out.print("hobby: ");
    46         for (int i = 0; i < hobby.length; i++) {
    47             System.out.print(hobby[i]);
    48             if (i != hobby.length - 1) {
    49                 System.out.print(", ");
    50             }
    51         }
    52         return SUCCESS;
    53     }
    54 }

    前台输入信息:

    后台显示信息:

    2、自定义类型转换

      1)基于OGNL的类型转换器

      Struts2的类型转换器实际上是基于OGNL实现的,都需要实现一个TypeConverter接口,该接口位于ognl.jar包内。该接口定义了一个convertValue()方法,实现该接口的类型转换器实现类都需要重写该方法来进行类型转换。

    1 public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);

      由于TypeConverter接口的convertValue()方法过于复杂,OGNL还提供了一个实现TypeConverter接口的类DefaultTypeConverter,开发者只要继承该类,就可以开发类型转换器的实现类。DefaultTypeConverter类的子类需要重写convertValue()方法,来实现字符串类型与复合类型之间的双向转换,convertValue()方法有三个参数:Map context:该参数为类型转换环境的上下文内容;Object value:该参数为需要转换的参数,是一个字符串数组;Class toType:该参数指的是转换目标的类型。

      在Struts2中并没有直接使用ognl.DefaultTypeConverter实现类,甚至都没有使用ognl.TypeConverter接口,而是自己定义了一个与ognl.TypeConverter接口一样的TypeConverter接口,该接口位于com.opensymphony.xwork2.conversion包下,其名称和接口函数定义完全相同。Struts2自行定义TypeConverter接口的目的在于对外屏蔽类型转换的实现细节,从而能够将Struts2对TypeConverter的扩展实现纳入到Struts2的容器中进行管理,从而方便对OGNL原始的TypeConverter接口进行扩展并支持更加广泛的类型转换逻辑。

      在Struts2的com.opensymphony.xwork2.conversion包下DefaultTypeConverter实现类有3个convertValue方法:

    所以我们在继承com.opensymphony.xwork2.conversion包下DefaultTypeConverter实现类的时候可以直接重写convertValue(Object value, Class toType)方法即可。

    无论是继承ongl包下的DefaultTypeConverter还是继承com.opensymphony.xwork2.conversion包下DefaultTypeConverter,在自定义类型转换的时候,实现方式都一样。

      2)基于Struts2的类型转换器

      Struts2框架提供了一个类型转换器的StrutsTypeConverter抽象类,可以继承该类来开发自定义的类型转换器实现类。 该抽象类实际上继承了DefaultTypeConverter类,在该类的基础上进行了简化。StrutsTypeConverter类中提供了两个抽象方法,分别实现"form字符串参数-Struts复合类型"之间的双向转换。

    3、注册类型转换器

    Struts2框架中使用自定义类型转换器需要注册,这样Struts2框架在处理用户请求的时候才知道使用哪个类型转换器进行转换。Struts2有两种方式注册类型转换器:

     1)注册局部类型转换器

      局部类型转换器仅仅对某个Action的属性其作用,注册局部类型转换器需要建立一个命名规则为ActionName-conversion.properties的属性文件,该属性文件保存在与Action类文件相同的目录下。ActionName就是使用类型转换器的Action类的类名,而-conversion.properties是固定的格式。该文件是一个标准的属性文件,内容为标准的Key-Value格式,该键值对定义如下:

    1 <!--propertyName为要进行转换的属性名-->
    2 propertyName=类型转换器

    如果在model中有两个类User和Product,它们都有Date类型属性,都需要在页面上输入,但是它们格式不一样,例如:

      User birthday 格式:yyyy/MM/dd

      Product producttime 格式:yyyy-MM-dd

    如果遇到这种情况,可以在model所在的包下创建注册的properties文件,名称的命名规则为modelname-conversion.properties,该属性文件的内容和上面的一样:

    1 <!--propertyName为要进行转换的属性名-->
    2 propertyName=类型转换器

     2)注册全局类型转换器

      全局类型转换器对所有Action的特定属性都会生效,注册一个全局类型转换器,需要建立一个xwork-conversion.properties属性文件,该文件需要保存在class路径的根目录下,如WEB-INF/classes。该文件的内容为"复合类型-对应的类型转换器",其中复合类型就是Action中需要进行类型转换的属性所属于的类型,对应的类型转换器就是转换该复合类型的对应转换器,比如要对Date类型的属性进行转换:

    java.util.Date=com.sunny.converter.DateConverter

    4、类型转换器的实现

    在类型转换器的实现例子中自定义类型转换器继承的是DefaultTypeConverter,注册局部类型转换器。

    表单信息:

    1 <form action="${pageContext.servletContext.contextPath}/converterAction.action">
    2     name:<input type="text" name="name"><br>
    3     password:<input type="password" name="password"><br>
    4     age:<input type="text" name="age"><br>
    5     birthday:<input type="text" name="birthday"><br>
    6     hobby:<input name="hobby" type="checkbox" value="music">music
    7         <input name="hobby" type="checkbox" value="movie">movie<br>
    8     <input type="submit" value="提交">
    9 </form>

    自定义类型转换器类:

     1 public class DateConverter extends DefaultTypeConverter {
     2     @Override
     3     public Object convertValue(Object value, Class toType) {
     4         if (value == null || toType == null) {
     5             return false;
     6         }
     7         
     8         if (toType != Date.class) {
     9             return false;
    10         }
    11         
    12         /*
    13          * 对于DefaultTypeConverter转换器而言,它必须考虑到最通用的情形,
    14          * 因此他把所有请求参数都视为字符串数组而不是字符串。
    15          */
    16         if (value instanceof String[]) {
    17             String str[] = (String[])value;
    18             if (str[0] != null && str[0].length() > 0) {
    19                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    20                 try {
    21                     //把请求参数转换成特定格式的date类
    22                     return sdf.parse(str[0]);
    23                 } catch (ParseException e) {
    24                     /*
    25                      *  在struts2框架里,自定义的类型转换器,
    26                      *  如果我们不手动抛出异常,struts2框架只捕获异常,但是并不抛出。
    27                      *  所以框架就会认为类型转换器转换成功,转向成功页面。
    28                      */
    29                     throw new RuntimeException(e);
    30                 }
    31             }
    32         }
    33         return new Date();
    34     }
    35 }

    注册局部类型转换器,对Date类型的birthday进行转换,接收yyyy/MM/dd类型字符串:

    1 birthday=com.sunny.converter.DateConverter

    Action类:

     1 public class ConverterAction extends ActionSupport {
     2     private String name;
     3     private String password;
     4     private int age;
     5     private Date birthday;
     6     private String[] hobby;
     7     
     8     public String getName() {
     9         return name;
    10     }
    11     public void setName(String name) {
    12         this.name = name;
    13     }
    14     public String getPassword() {
    15         return password;
    16     }
    17     public void setPassword(String password) {
    18         this.password = password;
    19     }
    20     public int getAge() {
    21         return age;
    22     }
    23     public void setAge(int age) {
    24         this.age = age;
    25     }
    26     public Date getBirthday() {
    27         return birthday;
    28     }
    29     public void setBirthday(Date birthday) {
    30         this.birthday = birthday;
    31     }
    32     public String[] getHobby() {
    33         return hobby;
    34     }
    35     public void setHobby(String[] hobby) {
    36         this.hobby = hobby;
    37     }
    38     
    39     @Override
    40     public String execute() throws Exception {
    41         System.out.println("name: " + name);
    42         System.out.println("password: " + password);
    43         System.out.println("age: " + age);
    44         System.out.println("birthday: " + birthday);
    45         System.out.print("hobby: ");
    46         for (int i = 0; i < hobby.length; i++) {
    47             System.out.print(hobby[i]);
    48             if (i != hobby.length - 1) {
    49                 System.out.print(", ");
    50             }
    51         }
    52         return SUCCESS;
    53     }
    54 }

    前台输入信息:

    后台显示信息:

    注意事项:

    • 在进行自定义类型转换时,如果转换错误,我们不手动抛出异常,struts2框架只捕获异常,并不抛出,框架就会认为转换成功,转向成功页面,所以一定要手动抛出异常。
    • 不管是自动类型转换还是自定义类型转换,如果类型转换错误,会跳转到input视图,如果在struts.xml文件中没有name值为"input"的<result>,会跳转到错误页面。

    5、错误处理机制

     Struts2的错误处理是由conversionError拦截器自动完成的,当发生类型转换错误时,conversionError拦截器拦截此错误并封装成fieldError,将此错误信息放入ActionContext中,并返回input逻辑视图,我们前面提到过此时必须在struts.xml文件中有name值为"input"的<result>,在jsp页面中可以使用<s:fielderror/>即可显示错误信息,默认显示的错误信息是在属性文件xwork-messages.properties中定义的:

    1 xwork.error.action.execution=Error during Action invocation
    2 xwork.exception.missing-action=There is no Action mapped for action name {0}.
    3 xwork.exception.missing-package-action=There is no Action mapped for namespace {0} and action name {1}.
    4 xwork.default.invalid.fieldvalue=Invalid field value for field "{0}".

    当birthday输入格式错误时,根据action中的<result name="input">/error.jsp</result>会跳转到error.jsp页面,

    jsp页面信息:

    1 <body>
    2 <s:fielderror fieldName="birthday"/>
    3 </body>

    jsp页面显示信息:

    如果我们需要配置显示的错误信息,有方式有两种:
    1)在国际化资源文件中配置
    xwork.default.invalid.fieldvalue={0},错误;
    在项目中建立一个国际化资源文件converter.properties,文件名可以随便,在文件中输入下面内容:
    xwork.default.invalid.fieldvalue={0},错误;
    然后,在struts.xml文件中配置<constant name="struts.custom.i18n.resources" value="com.sunny.action.converter" />
     1 <struts>
     2     <constant name="struts.devMode" value="true" />
     3     <constant name="struts.custom.i18n.resources" value="com.sunny.action.converter" />
     4     <package name="default" namespace="/" extends="struts-default">
     5         <action name="converterAction" class="com.sunny.action.ConverterAction">
     6             <result>/success.jsp</result>
     7             <result name="input">/error.jsp</result>
     8         </action>
     9     </package>
    10 
    11 </struts>

    说明:资源文件的定义有好几种方式,会在国际化中讲到

    如果birthday输入格式错误,显示如下:

    2)配置Action特定属性错误信息
    在Action所在包中,创建ActionName.properties,在文件中配置提示信息: invalid.fieldvalue.属性名= 错误信息,其中invalid.fieldvalue为固定格式
    invalid.fieldvalue.birthday=日期类型转换错误,要求格式必须是yyyy/MM/dd;
    显示效果:
    这种配置方式的内容:invalid.fieldvalue.birthday=日期类型转换错误,要求格式必须是yyyy/MM/dd;也可以配置在国际化资源文件中,同样可以起作用。
    注意:类型转换的Action在struts.xml中配置所在包需要extends="struts-default",因此在此文件中包含conversionError拦截器
  • 相关阅读:
    Python的网络编程[0] -> socket[1] -> socket 模块
    Python的网络编程[0] -> socket[0] -> socket 与 TCP / UDP
    Python的功能模块[4] -> pdb/ipdb -> 实现 Python 的单步调试
    Python的功能模块[3] -> binascii -> 编码转换
    Python的功能模块[2] -> abc -> 利用 abc 建立抽象基类
    Python的功能模块[1] -> struct -> struct 在网络编程中的使用
    Python的功能模块[0] -> wmi -> 获取 Windows 内部信息
    Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析
    Python的程序结构[7] -> 生成器/Generator -> 生成器浅析
    Python的程序结构[6] -> 迭代器/Iterator -> 迭代器浅析
  • 原文地址:https://www.cnblogs.com/xiaolongSunny/p/4655546.html
Copyright © 2011-2022 走看看