您的位置:首页 > 编程语言 > Java开发

springMVC4(10)强大类型转换器实例解析

2016-04-04 15:28 477 查看
《springMVC4(9)属性编辑器剖析入参类型转换原理 》一文中,我们通过分析Sping内置的属性编辑器来理解springMVC是如何完成请求参数到入参的类型的转换的。而在新版本中,SpringMVC使用了新的架构来完成类型转换的工作,而且它的工作更加强大,支持格式化参数输入输出,它的另一个实例可见我的另一篇文章《springMVC4(4)json与对象互转实例解析请求响应数据转换器》。在文中,我们使用了Spring内置的格式转换器完成了服务端输入输出过程中json字符串与java对象的相互转换。此外,还提到了其他多种的格式转换器,如xml、ByteArray等。下面,我们以自定义格式转换器的实现思路,来理解新架构的类型转换器的使用方法,同时在实际开发中,我们可能会有自己的格式转换需求,这个时候我们也可以通过自定义格式转换器来完成这些个性化需求。

自定义格式转换器

完成自定义转换器需要实现以下三个中的任意一个接口:
Convertor<S,T>
、GenericConvertor或ConvertorFacoty。下面我们对这些接口进行逐一分析:

1.
Convertor<S,T>

这是最为简单的一个接口,定义了从源类到目标类的转换方法。该接口的定义如下

public interface ConverterFactory<S, R> {
//将S类型的对象转换为T类型,R为目标类型T的基类
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}


2. GenericConvertor

GenericConvertor会根据源类对象及目标类对象所在宿主类的上下文信息进行类型转换工作,该接口的定义如下:

public interface GenericConverter {

//ConvertiblePair包含了源类型和目标类型,它的定义在下面
Set<ConvertiblePair> getConvertibleTypes();

//TypeDescriptor包含了需转换类型对象所在宿主类的信息,我们根据此信息,完成源到目标类型的转换
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

/**
* 内部类定义
*/
public static final class ConvertiblePair {
//源类型
private final Class<?> sourceType;
//目标类类型
private final Class<?> targetType;

/**
* 创建一个源-目标对子
*/
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}

public Class<?> getSourceType() {
return this.sourceType;
}

public Class<?> getTargetType() {
return this.targetType;
}
//忽略hashCode\equals\toString等重写方法
}
}


我们常使用其实现类接口:

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}


它除了实现GenericConverter,还实现了另一个“条件转换器”:

public interface ConditionalConverter {
/**
* Should the conversion from {@code sourceType} to {@code targetType} currently under
*/
//根据源类型和目标类型所在宿主类型的上下文信息判断是否要进行类型转换
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}


在实际开发中,我们能实现此接口自定义转换器,来根据具体类型上下文来灵活配置我们的类型转换

3. ConvertorFacoty

这是一个将我们源类转换为一个目标类或其子类的”多转换器共存“接口工厂。它的定义如下:

public interface ConverterFactory<S, R> {

//获取将源类转换为特定R类或其子类的转换器
<T extends R> Converter<S, T> getConverter(Class<T> targetType);

}


这个接口一个常见的实现类是StringToNumberConvertor,能将String类型数据转换为Number类型或其子类:Long,Integer,Double等。

注册自定义转换器

ConversionService

ConversionService则是Spring类型转换体系的核心接口,ConversionService接口的定义如下:

package org.springframework.core.convert;

public interface ConversionService {

//判断sourceType是否可以转换为targetType
boolean canConvert(Class<?> sourceType, Class<?> targetType);

//TypeDescriptor描述了转换类的各类上下文信息,在类型转换实现方法中可以根据这些信息进行灵活控制
//比如这里通过源类和目标类的上下文信息判断是否可以进行转换
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

//将source转换为targetType
<T> T convert(Object source, Class<T> targetType);

//利用源、目标类的上下文信息,将源类型转换为目标类型
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}


ConversionServiceFactoryBean

实现以上类型完成我们的自定义转换器定义后,我们还要在Spring容器中通过ConversionServiceFactoryBean注册创建后才能使用。

ConversionServiceFactoryBean创建了我们的ConversionService很多内置转换器,利用这些转换器,我们可以完成大部分常见的类型转换工作

而如果我们想使用自定义的类型转换器,可以通过ConversionServiceFactoryBean的convertor属性来注册。

实例分析1:测试Convertor

通过以上的分析,我们接下来尝试自定实现Convert

1. 自定义属性转换器

public class MyConvertor implements Converter<String, User>{

@Override
public User convert(String source) {//source为要转换的字符串
String[] values = source.split(",");//根据我们的需求,用逗号来区分
Integer id = Integer.valueOf(values[0]);
User user = new User(id,values[1],values[2]);
return user;
}
}
/**********下面是我们的UserPOJO类**********/
public class User {
public User() {
super();
}
private Integer id;
private String userName;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public User(Integer id, String userName, String password) {
super();
this.id = id;
this.userName = userName;
this.password = password;
}
//忽略get和set方法
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", password="
+ password + "]";
}

}


2. 注册自定义属性转换器

<!-- 通过:annotation-driven的conversion-service属性来装配我们的类型转换器 -->
<mvc:annotation-driven conversion-service="factoryBean" />
<!-- 通过ConversionServiceFactoryBean注册我们的自定义转换器 -->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="factoryBean" >
<property name="converters"><!-- 在属性converters注册 -->
<list>
<bean class="com.mvc.convertor.MyConvertor" />
</list>
</property>
</bean>


3. 配置控制器

在控制层,我们通过以下方法测试我们的转换器

@RequestMapping("convert")
public String convert(User user){
System.out.println(user);
return "model1";
}


4. 测试

启动服务器,在游览器中访问[项目根路径]/convert?user=11,myUserName,myPassword。

控制台会打印信息:User [id=11, userName=myUserName, password=myPassword]。即springMVC帮我们完成了字符串到User类型的转换。**这里需注意的是,我们的请求参数名”user”是和控制层方法入参变量User user像对应的,才能完成参数绑定进而转换类型

实例分析2:测试ConvertorFactory

1. 自定义类型转换器

在实例1的基础上,我们添加User的一个子类:SuperUser,作为”super”子类,它拥有了自己的专属名字,我们将字符串”11,myUserName,myPassword,myName“转换为我们的superUser对象,下面相对应的自定义转换器和POJO类

public class MySuperConvertor implements Converter<String, SuperUser>{

@Override
public SuperUser convert(String source) {
String[] values = source.split(",");
Integer id = Integer.valueOf(values[0]);
SuperUser superUser = new SuperUser(values[3],  new User(id,values[1],values[2]));
return superUser;
}
}
/**********下面是SuperUser POJO类*********/
package com.mvc.model;

public class SuperUser extends User {
private String name;
//忽略get和set方法

public SuperUser(String name,User user) {
super(user.getId(),user.getUserName(),user.getPassword());
this.name = name;
}

public SuperUser() {
super();
}

@Override
public String toString() {
return "SuperUser [name=" + name + ", toString()=" + super.toString()
+ "]";
}
}


除了配置上面的转换器,还需自定义我们的转换器工厂,在转换器工厂中,我们根据目标类型是User还是其子类SuperUser来调用相应的自定义转换器:

public class MyConvertorFactory implements ConverterFactory<String, User>{

@Override
//T类型必须是User或其子类,Stirng是我们的转换源类
public <T extends User> Converter<String, T> getConverter(

Class<T> targetType) {
if(targetType == User.class){
return  (Converter<String, T>) new MyConvertor();
}else{
return (Converter<String, T>) new MySuperConvertor();
}
}
}


2. 注册自定义属性转换器

<!-- 通过:annotation-driven的conversion-service属性来装配我们的类型转换器 -->
<mvc:annotation-driven conversion-service="factoryBean" />
<!-- 通过ConversionServiceFactoryBean注册我们的自定义转换器 -->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="factoryBean" >
<property name="converters"><!-- 在属性converters注册 -->
<list>
<!--这里只要注册我们自定义的转换器工厂即可-->
<bean class="com.mvc.convertor.MyConvertorFactory" />
</list>
</property>
</bean>


3. 配置控制器

在实例1的基础上,我们添加一个新方法

//这是原来的
@RequestMapping("convert")
public String convert( User user){
System.out.println(user);
return "model1";
}
//下面是新添加的方法
@RequestMapping("convertSuper")
public String convert( SuperUser user){
System.out.println(user);
return "model1";
}


4. 测试

运行服务器,我们在游览器中输入:

1. root/convert?user=10,myUserName,myPassword

控制台输出:User [id=10, userName=myUserName, password=myPassword]

2. root/convertSuper?superUser=11,myUserName,myPassword,myName

控制台输出:SuperUser [name=myName, toString()=User [id=11, userName=myUserName, password=myPassword]]

我们根据入参类型,并通过ConvertFactory,完成对同一系列(某一类及其子类)的类型转换

源码下载

本篇文章测试源码可到https://github.com/jeanhao/spring的dataConvertor文件夹下下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: