大部分时候,使用Struts2框架提供的内建的类型转换器和基于OGNL的类型转换器,就能满足大部分的类型转换需求;但是也存在一些特殊的情况下,可能需要将一个指定格式的字符串转换成一个复合对象,此时就需要使用自定义类型转换器。比如,客户端传送一个"jelly|123456"这样的字符串到服务器,我们需要将这个字符串转换成一个User类型实例,其中“jelly”作为用户名,“123456”作为用户密码。
有这样的一个前台页面:
<form action="login" method="post"> 用户:<input type="text" name="userinfo"><br> <input type="submit" value="转换"> </form>
对应Action的部分代码为:
private User user; // 这是一个复合类型 private String tip; public void setUsers(User user) { this.user = user; } public User getUser() { return user; }
我们希望用户在前台页面输入格式为“username|password”这样的字符串,通过自定义的类型转换器,将这样“username|password”格式的字符串转换为User类型。下面就说说如何实现自定义类型转换器。
Struts2的类型转换器实际上依然是基于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
方法。
下面就是转换为User类实例的转换类:
public class UserConverter extends DefaultTypeConverter { public Object convertValue(Map context, Object value, Class toType) { if (toType == User.class) { // 当需要将字符串向User类型转换时 // 系统的请求参数是一个字符串数组 String[] params = (String[])value; // 创建一个User实例 User user = new User(); // 只处理请求参数数组第一个数组元素,并将该字符串以'|'符号分割成两个字符串 String[] userValues = params[0].split("//|"); // 为User实例赋值 user.setName(userValues[0]); user.setPassword(userValues[1]); return user; } else if (toType == String.class) { // 当需要将User类型向字符串转换时 User user = (User)value; return "<" + user.getName() + "|" + user.getPassword() + ">"; } return null; } }
convertValue
方法的作用是非常简单的,该方法负责完成类型的转换,但是这种转换是双向的,根据参数 toType
来判断目标类型,然后我们就可以实现两个方向的转换逻辑了。
有了类型转换器是不够的,因为Struts2框架还不知道何时使用这些类型转换器,所以必须将类型转换器注册在Web应用中,Struts2框架才可以正常使用该类型转换器。
Struts2主要支持以下两种注册类型转换器的方式:
下面就来分别说说以上两种注册类型转换器的方法。
Struts2允许开发者通过局部类型转换文件来指定类型转换的相关配置信息。类型转换文件就是一个普通的Properties(*.properties)文件。局部类型转换文件的文件名应为 ActionName-conversion.properties
形式,其中ActionName是需要使用该类型转换文件的Action名,后面的-conversion.properties字符串则是固定的部分。类型转换文件应该放在和Action类文件相同的位置。
说完局部类型转换文件,就接着说这个注册局部类型转换器。注册局部类型转换器只需要在局部类型转换文件中增加以下一行配置即可:
<propName>=<ConverterClass>
将上面的 <propName>
替换成需要进行类型转换的属性、 <ConverterClass>
替换成类型转换器的实现类即可。对于上面实现的 UserConverter
类,需要在局部类型转换文件中这样配置:
# 指定user属性需要使用UserConverter类来完成类型转换 user=com.jellythink.practise.UserConverter
局部类型转换器的局限性太明显了,它只能对指定Action、指定属性起作用。但是,如果应用中有多个Action都包含了User类型的属性,或者一个Action中包含了多个User类型的属性,使用全局类型转换器将更合适。
全局类型转换器不是对指定Action的指定属性起作用,而是对指定类型起作用。比如对所有类型为 com.jellythink.practise.User
类型的属性起作用。
注册全局类型转换器应该提供一个 xwork-conversion.properties
文件,该文件就是全局类型转换文件,需要将该文件直接放在Web应用的 WEB-INF/classes
路径下即可。
全局类型转换文件内容由多项 <propType>=<ConvertClass>
项组成,将 propType
替换成需要进行类型转换的类型,将 ConvertClass
替换成类型转换器的实现类即可。对于上面实现的 UserConverter
类,需要在全局类型转换文件中这样配置:
com.jellythink.practise.User=com.jellythink.practise.UserConverter
局部类型转换只能对指定Action的指定属性进行转换,不管该Action的该属性是数组也好,是集合类型也罢,该转换器的转换方法对该属性只转换一次。如果某个Action有个 List<User>
类型的属性users,那么局部类型转换器将只调用一次 convertValue()
方法,该方法把users请求参数一次性地转换为一个 List<User>
集合对象。
全局类型转换器会对所有Action的特定类型进行转换,如果一个Action的某个属性是数组或集合类型,而数组或集合元素是需要该转换器转换的方法,那么全局类型转换器将不是对该集合属性整体进行转换,而是对该集合属性的每个元素进行转换,也就是说,当该属性是一个数组或集合时,该数组或集合中包含几个该类型的元素,那么就会调用 convertValue()
方法几次。
熟悉二者的区别以后,在以后工作中就的多多注意了;特别是数组和集合元素进行转换时的区别。
上面介绍了通过继承 DefaultTypeConverter
类,在一个 convertValue
方法中,通过判断toType参数来判断转换的方向,然后分别实现不同转换方向的转换逻辑。而在Struts2框架中,为了简化类型转换器的实现,特别提供了一个 StrutsTypeConverter
抽象类,该类是DefaultTypeConverter类的子类。它已经实现了 DefaultTypeConverter
类的 convertValue
方法;实现该方法时,它将以前在 convertValue
方法中通过判断toType参数的方式来实现两个不同方向的转换变成了调用以下两个不同的方法:
convertFromString
抽象方法 convertToString
抽象方法
而我们需要做的就是继承 StrutsTypeConverter
抽象类,并且实现 convertFromString
和 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.setPassword(userValues[1]); return user; } public String convertToString(Map context, Object o) { // 当需要将User类型向字符串转换时 User user = (User)o; return "<" + user.getName() + "|" + user.getPassword() + ">"; } }
这篇文章对Struts2中的自定义类型转换器进行了详细的总结,总体思路如下:
就这几步就搞定了自定义类型转换器喽。
果冻想,认真玩技术的地方。
2016年3月20日 于呼和浩特。