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

简介: <div class="markdown_views"><p>在<a href="http://blog.csdn.net/qwe6112071/article/details/51049743">《springMVC4(9)属性编辑器剖析入参类型转换原理 》</a>一文中,我们通过分析Sping内置的属性编辑器来理解springMVC是如何完成请求参数到入参的类型的转换的

《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文件夹下下载

目录
相关文章
|
14天前
|
存储 缓存 安全
掌握Go语言:Go语言中的字典魔法,高效数据检索与应用实例解析(18)
掌握Go语言:Go语言中的字典魔法,高效数据检索与应用实例解析(18)
|
21天前
|
XML 存储 Java
Spring重要类解析
Spring重要类解析
19 0
|
23天前
|
机器学习/深度学习 算法 编译器
【C++ 泛型编程 中级篇】深度解析C++:类型模板参数与非类型模板参数
【C++ 泛型编程 中级篇】深度解析C++:类型模板参数与非类型模板参数
46 0
|
1月前
|
消息中间件 Cloud Native Java
【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合
【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合
|
19天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
42 1
|
23天前
|
存储 JSON 安全
【C++ 泛型编程 综合篇】泛型编程深度解析:C++中的五种类型泛型策略综合对比
【C++ 泛型编程 综合篇】泛型编程深度解析:C++中的五种类型泛型策略综合对比
65 1
|
5天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
14 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
5天前
|
XML Java 数据格式
从入门到精通:Spring基础注解的全面解析
从入门到精通:Spring基础注解的全面解析
22 2
从入门到精通:Spring基础注解的全面解析
|
5天前
|
Java 数据库 Spring
切面编程的艺术:Spring动态代理解析与实战
切面编程的艺术:Spring动态代理解析与实战
19 0
切面编程的艺术:Spring动态代理解析与实战
|
5天前
|
Java 关系型数据库 MySQL
高级对象装配:解析Spring创建复杂对象的秘诀
高级对象装配:解析Spring创建复杂对象的秘诀
20 0
高级对象装配:解析Spring创建复杂对象的秘诀

推荐镜像

更多