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

目录
相关文章
|
1天前
|
存储 Java
JAVA中的变量:深入解析与实例
JAVA中的变量:深入解析与实例
9 3
|
4天前
|
存储 C语言 索引
C语言字符数组深入解析与应用实例
C语言字符数组深入解析与应用实例
10 0
|
4天前
|
存储 数据处理 Python
Python中的字典(Dictionary)类型:深入解析与应用
Python中的字典(Dictionary)类型:深入解析与应用
|
4天前
|
Java
【Java探索之旅】内部类 静态、实例、局部、匿名内部类全面解析
【Java探索之旅】内部类 静态、实例、局部、匿名内部类全面解析
5 0
|
8天前
|
存储 API Python
Python文件操作:深入解析与实例
Python文件操作:深入解析与实例
|
8天前
|
测试技术 持续交付 数据处理
Python动态类型深度解析与实践
Python动态类型深度解析与实践
395 1
|
9天前
|
机器学习/深度学习 前端开发 JavaScript
HTML基础 + 实例解析(速速来看!!!)
HTML基础 + 实例解析(速速来看!!!)
9 0
|
11天前
|
弹性计算 数据挖掘 应用服务中间件
阿里云服务器通用算力型U1实例解析,实例性能、适用场景及常见问题参考
在阿里云服务器的所有实例规格中,通用算力型u1实例主打的是高性价比,通用算力型U1实例云服务器自推出以来,就受到了广大用户的关注,也是目前阿里云的活动中比较热门的云服务器实例,这个实例规格的性能要好于经济型e等共享型实例,价格又比计算型c7、通用型g7等其他企业级实例要低一些。本文将深入解析通用算力型U1实例的特点、适用场景以及价格优势,帮助用户更好地了解该云服务器实例。
阿里云服务器通用算力型U1实例解析,实例性能、适用场景及常见问题参考
|
9天前
|
移动开发 网络协议 安全
HTML5页面被运营商DNS问题及解决方案,app中h5页面源码的获取
HTML5页面被运营商DNS问题及解决方案,app中h5页面源码的获取
64 4
|
9天前
|
域名解析 网络协议 应用服务中间件
2024最新彩虹聚合DNS管理系统源码v1.3 全开源
聚合DNS管理系统可以实现在一个网站内管理多个平台的域名解析,目前已支持的域名平台有:阿里云、腾讯云、华为云、西部数码、DNSLA、CloudFlare。 本系统支持多用户,每个用户可分配不同的域名解析权限;支持API接口,支持获取域名独立DNS控制面板登录链接,方便各种IDC系统对接。
59 0

推荐镜像

更多