大部分时候,我们使用Struts 2提供的类型转换器,以及基于OGNL的类型转换机制,就能满足大部分类型转换需求。但在有些特殊的情形下,例如需要把一个字符串转换成一个复合对象(例如 User对象)时,这就需要使用自定义类型转换器。例如,用户输入一个abc,xyz字符串,我们需要将其转换成一个User类型实例,其中abc作为User实例的name属性值,而xyz作为User实例的pass属性值。

假设本系统有一个如图4.2所示的表单输入页面。

 

自定义类型转换器_java

4.2  输入字符串的页面

如图4.2所示的页面包含一个名为user的表单域,这将产生一个名为user的请求参数,该请求对应的Action类代码如下。

程序清单:codes\04\4.1\LocalConverter\WEB-INF\src\org\crazyit\app\action\LoginAction.java

public class LoginAction implements Action

{

     private User user;

     private String tip;

     //user属性的settergetter方法

     public void setUser(User user)

     {

          this.user = user;

     }

     public User getUser()

     {

          return this.user;

     }

     //省略tip属性的settergetter方法

     ...

     public String execute() throws Exception

     {

          if (getUser().getName().equals("crazyit.org")

               && getUser().getPass().equals("leegang") )

          {

               setTip("登录成功!");

               return SUCCESS;

          }

          else

          {

               setTip("登录失败!!");

               return ERROR;

          }

     }

}

从上面的代码中可以看出,该Actionuser属性是User类型,而对应表单页发送的user请求参数则只能是字符串类型。Struts 2功能虽然强大,但它依然不知道如何完成字符串和User对象之间的转换,这种转换需要程序员来完成。

User类就是一个普通的JavaBean类,关于此类代码读者请参考光盘中的程序清单:codes\04\4.1\LocalConverter\WEB-INF\src\org\crazyit\app\domain\User.java

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

//OGNL提供的类型转换器接口

public interface TypeConverter

{

     public Object convertValue(Map context, Object target,

          Member member, String propertyName, Object value, Class toType);

}

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

下面是本应用所使用的类型转换器的代码。

程序清单:codes\04\4.1\LocalConverter\WEB-INF\src\org\crazyit\app\converter\UserConverter.java

public class UserConverter extends DefaultTypeConverter

{

     //类型转换器必须重写convertValue方法,该方法需要完成双向转换

     public Object convertValue(Map context

          , Object value, Class toType)

     {

          //当需要将字符串向User类型转换时

          if (toType == User.class )

          {

               //系统的请求参数是一个字符串数组

               String[] params = (String[])value;

               //创建一个User实例

               User user = new User();

               //只处理请求参数数组第一个数组元素,

               //并将该字符串以英文逗号分割成两个字符串

               String[] userValues = params[0].split(",");

               //User实例赋值

               user.setName(userValues[0]);

               user.setPass(userValues[1]);

               //返回转换来的User实例

               return user;

          }

          else if (toType == String.class )

          {

               //将需要转换的值强制类型转换为User实例

               User user = (User) value;

               return "<" + user.getName() + ","

                    + user.getPass() + ">";

          }

          return null;

     }

}

上面的程序的粗体字代码是实现类型转换的关键,第一段粗体字代码实现将字符串转换成User对象,第二段粗体字代码实现将User对象转换成字符串。读者可能对上面实现的类型转换器感到有一些迷惑,下面是关于上面的类型转换器的几点说明。

1convertValue方法的作用

 

convertValue方法的作用最简单,该方法负责完成类型的转换,不过这种转换是双向的:当需要把字符串转换成User实例时,是通过该方法实现的;当需要把User实例转换成字符串时,也是通过该方法实现的。

为了让该方法实现双向转换,我们通过判断toType的类型即可判断转换的方向。toType类型是需要转换的目标类型,当toType类型是User类型时,表明需要将字符串转换成User实例;当toType类型是String类型时,表明需要把User实例转换成字符串类型。图4.3显示了这种toType参数和转换方向之间的关系。

 

自定义类型转换器_开发_02 

图4.3 toType参数和转换
    方向之间的关系
 

一旦通过toType类型判断了类型转换的方向后,我们就可以分别实现两个方向的转换逻辑了。

2convertValue方法参数和返回值的意义

通过上面的介绍可以看出,实现类型转换器的关键就是实现convertValue方法,该方法有如下三个参数。

Ø  第一个参数:context是类型转换环境的上下文。

Ø  第二个参数:value是需要转换的参数。随着转换方向的不同,value参数的值也是不一样的,当把字符串类型向User类型转换时,value是原始字符串数组;当需要把User类型向字符串类型转换时,valueUser实例。

Ø  第三个参数:toType是转换后的目标类型,这个参数前面已经介绍了。

该方法的返回值就是类型转换后的值,该值的类型也会随转换方向的不同而不同,当把字符串向User类型转换时,返回值类型就是User类型;当需要把User类型向字符串类型转换时,返回值类型就是字符串类型。

由此可见,转换器的convertValue方法,接收需要转换的值,需要转换的目标类型为参数,然后返回转换后的目标值。

3.当把字符串向User类型转换时,为什么value是一个字符串数组,而不是一个字符串

很多读者会感到疑惑:当我们需要把字符串转换成User类型时,为什么value的值是字符串数组,而不是一个字符串。因为在前面的介绍中,我们总是说浏览器发送的请求参数类型是字符串,而不是字符串数组。

在如图4.4所示的页面中,姓名输入框的值只能是一个普通字符串。但选择课程的列表框的值则可以同时选择多个值。因此,浏览者向服务器发送请求时,该下拉列表框对应的请求参数则是字符串数组。

 

 

自定义类型转换器_java_03

4.4  包含字符串数组请求参数的表单页

对于DefaultTypeConverter转换器而言,它必须考虑到最通用的情形,因此它把所有的请求参数都视为字符串数组,而不是字符串。对字符串请求参数而言(例如姓名请求参数),转换器把该请求参数值当成长度为1的数组。

 

提示:

 

 

 

可以认为DefaultTypeConverter是通过HttpServletRequestgetParameter Values(name)方法来获取请求参数值的。因此它获取的请求参数总是字符串数组,如果请求参数只包含一个单个的值,则该请求参数的值是一个长度为1的字符串数组。