SpringMVC中类型转换器Converter<S,T>详解

简介: SpringMVC中类型转换器Converter<S,T>详解

ConversionService是Spring类型转换体系的核心接口,可以利用conversionServiceFactoryBean在Spring工厂容器中定义一个conversionService。


Spring将自动识别出ConversionService,并在bean属性配置及SpringMVC处理方法入参绑定等场合使用它进行数据转换。


SpringMVC上下文中内建了很多转换器,可以完成大多数Java类型的转换工作。

【1】常见转换器接口


Spring定义了三种类型的转换器接口,实现任意一个接口都可以作为自定义转换器注册到ConversionServiceFactoryBean中。需要特别注意的是,这里的Convert接口与HttpMessageConverter接口截然不同,后者是用于从HTTP请求和响应转换为HTTP请求和响应的策略接口。

Converter

:将S类型对象转换为T类型对象,该接口的实现是线程安全的、可以共享的。

@FunctionalInterface
public interface Converter<S, T> {
  //返回T对象
    @Nullable
    T convert(S var1);
}


默认内置了如下转换器

ObjectToStringConverter (org.springframework.core.convert.support)
LocalDateTimeToLocalTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
StringToCharsetConverter (org.springframework.core.convert.support)
StringToPropertiesConverter (org.springframework.core.convert.support)
ConversionServiceConverter in ConvertingComparator (org.springframework.core.convert.converter)
CalendarToLocalDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
OffsetDateTimeToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToCharacterConverter (org.springframework.core.convert.support)
ZonedDateTimeToLocalDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
ZonedDateTimeToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
LongToDateConverter in DateFormatterRegistrar (org.springframework.format.datetime)
NumberToNumber in NumberToNumberConverterFactory (org.springframework.core.convert.support)
ZonedDateTimeToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
DateToLongConverter in DateFormatterRegistrar (org.springframework.format.datetime)
StringToTimeZoneConverter (org.springframework.core.convert.support)
DateTimeToLocalDateConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
ZoneIdToTimeZoneConverter (org.springframework.core.convert.support)
OffsetDateTimeToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
LocalDateTimeToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToBooleanConverter (org.springframework.core.convert.support)
InstantToLongConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
PropertiesToStringConverter (org.springframework.core.convert.support)
DateTimeToDateMidnightConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
OffsetDateTimeToLocalDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToReadableInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
EnumToStringConverter (org.springframework.core.convert.support)
NumberToCharacterConverter (org.springframework.core.convert.support)
IntegerToEnum in IntegerToEnumConverterFactory (org.springframework.core.convert.support)
LocalDateTimeToLocalDateConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
StringToUUIDConverter (org.springframework.core.convert.support)
DateTimeToInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
LongToReadableInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
ZonedDateTimeToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
OffsetDateTimeToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToCurrencyConverter (org.springframework.core.convert.support)
CalendarToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToLocaleConverter (org.springframework.core.convert.support)
DateTimeToMutableDateTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
DateTimeToDateConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
ZonedDateTimeToOffsetDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToNumber in StringToNumberConverterFactory (org.springframework.core.convert.support)
DeserializingConverter (org.springframework.core.serializer.support)
LongToCalendarConverter in DateFormatterRegistrar (org.springframework.format.datetime)
DateToCalendarConverter in DateFormatterRegistrar (org.springframework.format.datetime)
DateTimeToLongConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
DateTimeToLocalTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
LongToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CharacterToNumber in CharacterToNumberFactory (org.springframework.core.convert.support)
CalendarToLongConverter in DateFormatterRegistrar (org.springframework.format.datetime)
EnumToIntegerConverter (org.springframework.core.convert.support)
ZonedDateTimeToCalendarConverter (org.springframework.core.convert.support)
OffsetDateTimeToZonedDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToOffsetDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToEnum in StringToEnumConverterFactory (org.springframework.core.convert.support)
SerializingConverter (org.springframework.core.serializer.support)
DateTimeToLocalDateTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
DateTimeToCalendarConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
CalendarToDateConverter in DateFormatterRegistrar (org.springframework.format.datetime)
DateToReadableInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
CalendarToZonedDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
LocalDateTimeToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)


② ConverterFactory

将相同系列多个"同质" Converter封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将String转换为Number及Number的子类(Integer、Long、Double等)对象)。注解源码如下

public interface ConverterFactory<S, R> {
   //获取转换器,该转换器可以将S转换为目标T类型(T  extends R)
  <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}


其是典型的工厂模式,子类具体工厂(都是final不可继承)负责生产对应的Convert:


CharacterToNumberFactory:static final class CharacterToNumber,Character–Number;

StringToEnumConverterFactory :static class StringToEnum,String–Enum;

IntegerToEnumConverterFactory:static class IntegerToEnum,Integer–Enum;

NumberToNumberConverterFactory:static final class NumberToNumber,Number–Number;

StringToNumberConverterFactory:static final class StringToNumber,String–Number

final修饰类时表示该类不允许被继承,final类中的成员方法都会被隐式的指定为final方法。

static修饰类时只能修饰内部类,此时内部类可以直接创建实例、可以有静态成员变量、方法(普通内部类不行)和非静态成员变量、方法。

③ GenericConverter

用于在两个或多个类型之间转换的通用转换器接口。这是最灵活的转换器SPI接口,也是最复杂的。


它的灵活性在于,GenericConverter 可能支持在多个源/目标类型对之间进行转换。此外,GenericConverter 实现在类型转换过程中可以访问源/目标。这允许解析可用于影响转换逻辑的源和目标字段元数据,如注解和泛型信息。


当更简单的 Converter或ConverterFactory接口足够时,通常不应使用此接口。会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换。

public interface GenericConverter {
   //返回此转换器可以转换的源类型和目标类型。每一个实体都是一个可以转换的 source-to-target类型对
  @Nullable
  Set<ConvertiblePair> getConvertibleTypes();
   //将源对象转换为TypeDescriptor 描述的类型,返回转换后的对象
  @Nullable
  Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
  /**
   * Holder for a source-to-target class pair.
   */
  final class ConvertiblePair {
    private final Class<?> sourceType;
    private final Class<?> targetType;
  //...
  }
}

【2】自定义转换器

如果想把一个字符串转换成其它实体类型,spring没有提供这样默认的功能,我们需要自定义类型转换器。


① 自定义类型转换器

需求如下:这里有个实体类Employee,将传输参数(String类型)转换为Employee。

//实现了Converter<String, Employee> 接口
@Component
public class EmployeeConverter implements Converter<String, Employee> {
  @Override
  public Employee convert(String source) {
    if(source != null){
      String [] vals = source.split("-");
      //GG-gg@web.com-0-105
      if(vals != null && vals.length == 4){
        String lastName = vals[0];
        String email = vals[1];
        Integer gender = Integer.parseInt(vals[2]);
        Department department = new Department();
        department.setId(Integer.parseInt(vals[3]));
        Employee employee = new Employee(null, lastName, email, gender, department);
        System.out.println(source + "--convert--" + employee);
        return employee;
      }
    }
    return null;
  }
}


② 注册到容器

xml方式注册

可通过ConversionServiceFactoryBeanconverters属性注册自定义的类型转换器。示例如下:

<!-- 配置 ConversionService 数据类型的转换和格式化 -->
  <bean id="conversionService"
    class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
      <set>
        <ref bean="employeeConverter"/>
        <!-- 自定义转换器  以前的一样存在 -->
      </set>
    </property> 
  </bean>
<mvc:annotation-driven conversion-service="conversionService"/>
<!--将自定义的conversionService注册到Spring MVC的上下文中-->


上面配置文件中我们使用配置了一个FormattingConversionServiceFactoryBean。虽然可以使用ConversionServiceFactoryBean,但是不推荐。

SpringBoot下使用java config注册

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
       registry.addConverter(new EmployeeConverter());
        // ...
    }
}


【3】ConverterFactory使用实例

实践背景:根据code(字符串)转换为一个枚举对象。

① MyEnum

MyEnum实现了接口BaseEnum 。

public enum  MyEnum implements BaseEnum {
    enum1(1,"enum1"), enum2(2,"enum2");
    private int code;
    private String name;
    MyEnum(int code, String name) {
        this.code = code;
        this.name = name;
    }
    @Override
    public String toString() {
        return String.valueOf(code);
    }
    @Override
    public int code() {
        return code;
    }
}


② MyConver

MyConver将String转换为BaseEnum的子类。

@Component
public class MyConver<T extends BaseEnum> implements Converter<String, T> {
    @Override
    public T convert(String source) {
        switch (source){
            case "1":return (T)MyEnum.enum1;
            case "2":return (T)MyEnum.enum2;
        }
        return (T)MyEnum.enum1;
    }
}


③ MyConvertFactory

MyConvertFactory 的核心思想就是根据targetType获取对应的Converter。

@Component
public class MyConvertFactory implements ConverterFactory<String,BaseEnum> {
    @Override
    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
        if(targetType ==BaseEnum.class){
            return new MyConver<>();
        }
        return new MyConver<>();
    }
}

然后注册到Spring中:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
       registry.addConverterFactory(new MyConvertFactory());
        // ...
    }
}

实际开发中,常常会遇到“数据格式化”的场景。那么如何格式化数据?其实就是通过使用转换器将数据转换为目标格式。


【4】FormattingConversionService


Spring 在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类。该实现类扩展了GenericConversionService,因此它既具有类型转换的功能又具有格式化的功能。


FormattingConversionService拥有一个FormattingConversionServiceFactoryBean工厂类,后者用于在Spring上下文中构造前者。




FormattingConversionService内部注册了两个工厂类:


① NumberFormatAnnotationFormatterFactory:支持对数字类型的属性使用@NumberFormat注解;


② JodaDateTimeFormatAnnotationFormatterFactory:支持对日期类型的属性使用@DateTimeFormat注解。


这下就明白了,为什么可以使用@NumberFormat和@DateTimeFormat注解进行数据格式化了


装配了FormattingConversionServiceFactoryBean后,就可以在SpringMVC入参绑定及模型数据输出时使用注解驱动了。FormattingConversionServiceFactoryBean的afterPropertiesSet方法很有意思,在bean实例化后会获取DefaultFormattingConversionService实例,然后注册类型转换器和格式化器Formatter

public void afterPropertiesSet() {
    this.conversionService = new DefaultFormattingConversionService(this.embeddedValueResolver, this.registerDefaultFormatters);
    ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
    registerFormatters(this.conversionService);
  }

SpringMVC配置文件示例

<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>  
<!-- 配置 ConversionService 数据类型的转换和格式化 -->
<bean id="conversionService"
  class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
  <!--注意这里是FormattingConversionServiceFactoryBean-->
  <property name="converters">
    <set>
      <ref bean="employeeConverter"/>
      <!-- 自定义转换器  以前的一样存在 -->
    </set>
  </property> 
</bean>

【5】日期格式化和数值格式化

示例代码如下图所示:


下面分别简要介绍一下上述两个注解


① @DateTimeFormat注解

该注解可对java.util.Date , java.util.Calendar , java.long.Long 时间类型进行标注。

① pattern属性:


类型为字符串。指定解析/格式化字段数据的模式,如“yyyy-MM-dd HH:mm:ss”。

注意:如果是12小时制,使用 hh:mm:ss ; 如果是24小时制,使用HH:mm:ss。


② iso属性

类型为DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:

ISO.NONE;
ISO.DATE(yyyy-MM-dd);
ISO.TIME(hh:mm:ss.SSSZ);
ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)。

③ style属性

字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式。

  • S:短日期/时间格式;
  • M:中日期/时间格式;
  • L:长日期/时间格式;
  • F:完整日期/时间格式;
  • -:忽略日期或时间格式。


② @NumberFormat注解

该注解可对类似数字类型的属性进行标注,它有两个互斥的属性:

① style


类型为NumberFormat.Style。用于指定样式类型,包括三种:

Style.NUMBER(正常数字类型);
Style.CURRENCY(货币类型);
Style.PERCENT(百分数类型)。


② pattern

类型为String,自定义样式,如pattern="#,###"

目录
相关文章
|
7月前
|
JSON 前端开发 Java
解决Spring MVC中No converter found for return value of type异常
在Spring MVC开发中遇到`No converter found for return value of type`异常,通常是因缺少消息转换器、返回值类型不支持或转换器优先级配置错误。解决方案包括:1) 添加对应的消息转换器,如`MappingJackson2HttpMessageConverter`;2) 自定义消息转换器并实现`HttpMessageConverter`接口,设置优先级;3) 修改返回值类型为如`ResponseEntity`的合适类型。通过这些方法可确保返回值正确转换为响应内容。
626 1
|
前端开发 Java Spring
Spring MVC-06循序渐进之Converter和Formatter
Spring MVC-06循序渐进之Converter和Formatter
100 0
SpringMVC - 数据绑定(自定义数据转换器:PropertyEditor、Formatter、Converter)(三)
SpringMVC - 数据绑定(自定义数据转换器:PropertyEditor、Formatter、Converter)(三)
171 0
|
JSON 监控 前端开发
SpringMVC使用fastjson自定义Converter支持返回jsonp格式(转)
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.
2093 0
|
前端开发
SpringMvc 类型转换 Converter
springMvc 怎么使用类型转换的呢 ,实现 Converter 接口 最长用的 是时间类型的转换 否则 400 controller 接受 /** * 接收日期类型参数 * 注意: * springm...
1059 0
|
前端开发 Java Spring
SpringMVC之类型转换Converter
SpringMVC之类型转换Converter 1.1     目录 1.1      目录 1.2      前言 1.3      Converter接口 1.4      ConversionService接口 1.
1059 0
|
7月前
|
设计模式 前端开发 Java
了解 Spring MVC 架构、Dispatcher Servlet 和 JSP 文件的关键作用
Spring MVC 是 Spring 框架的一部分,是一个 Web 应用程序框架。它旨在使用 Model-View-Controller(MVC) 设计模式轻松构建Web应用程序。
116 0
|
前端开发 Java 程序员
38SpringMVC - SpringMVC架构
38SpringMVC - SpringMVC架构
50 0
|
2月前
|
存储 设计模式 前端开发
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
文章解释了SpringMVC的概念和各部分功能,探讨了应用分层的原因和具体实施的三层架构,以及SpringMVC与三层架构之间的关系和联系。
25 1
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
|
6月前
|
安全 前端开发 Java
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
57 1
下一篇
DataWorks