目录

Spring Framework 类型转换

Spring Framework里贯穿其上下文,具有举足轻重地位的一个模块:类型转换(也可叫数据转换)。

Java是个多类型且强类型语言,类型转换这个概念对它来说并不陌生。

  • 自动类型转换(隐式):小类型 -> 大类型。eg:int a = 10; double b = a;
  • 强制类型转换(显式,精度丢失):大类型 -> 小类型。eg:double a = 10.123; int b = (int)a;
  • 调用API类型转换:常见的是字符串和其它类型的互转。eg:Integer.parseInt(String); new ObjectMapper().writeValueAsString(Obj); LocalDate.parse(String); API 可以来自于 JDK 提供、一方库、二方库、三方库提供。

PropertyEditor

早期的 Spirng(3.0之前)类型转换是基于 Java Beans 接口 java.beans.PropertyEditor 来实现的,全部继承自它的实现 PropertyEditorSupport

基于PropertyEditor的类型转换,职责不单一,类型不安全,只能实现String类型的转换等。虽然自Spring 3.0起提供了现代化的类型转换接口,但是此部分机制一直得以保留,保证了向下兼容性。

Converter ConverterFactory GenericConverter

  • Converter<S, T> Source -> Target 类型转换接口,适用于1:1转换
    • StringToPropertiesConverter:将String类型转换为Properties
    • StringToBooleanConverter:将String类型转换为Boolean
    • EnumToIntegerConverter:将Enum类型转换为Integer
  • ConverterFactory<S, R> Source -> R 类型转换接口,适用于1:N转换
    • StringToEnumConverterFactory:将String类型转任意Enum
    • StringToNumberConverterFactory:将String类型转为任意数字(可以是int、long、double等等)
    • NumberToNumberConverterFactory:数字类型转为数字类型(如int到long,long到double等等)
  • GenericConverter 更为通用的类型转换接口,适用于N:N转换
    • ObjectToCollectionConverter:任意集合类型转为任意集合类型(如List转为List / Set都使用此转换器)
    • CollectionToArrayConverter:解释基本同上
    • MapToMapConverter:解释基本同上
  • ConditionalConverter 条件转换接口。可跟上面3个接口组合使用,提供前置条件判断验证

转换服务接口 ConversionService

ConversionService 接口也是 Spring 3.0 新增,用于统一化 底层类型转换实现的差异,对外提供统一服务,所以它也被称作类型转换的门面接口。

主要实现:

  • GenericConversionService 提供模版实现,如转换器的注册、删除、匹配查找等,但并不内置转换器实现
  • DefaultConversionService 继承自 GenericConversionService。在它基础上默认注册了非常多的内建的转换器实现,从而能够实现绝大部分的类型转换需求

ConversionService 转换服务它贯穿于 Spring 上下文 ApplicationContext 的多项功能,包括但不限于:BeanWrapper 处理 Bean 属性、DataBinder 数据绑定、PropertySource 外部化属性处理等等。

Formatter

Spring 3.0 还新增了一个 Formatter<T> 接口,作用为:将Object格式化为类型T。从语义上理解它也具有类型转换(数据转换的作用),相较于 Converter<S,T> 它强调的是格式化,因此一般用于时间/日期、数字(小数、分数、科学计数法等等)、货币等场景,举例它的实现:

  • DurationFormatter:字符串和Duration类型的互转
  • CurrencyUnitFormatter:字符串和javax.money.CurrencyUnit货币类型互转
  • DateFormatter:字符串和 java.util.Date 类型互转。

为了和类型转换服务 ConversionService 完成整合,对外只提供统一的 API。Spring 提供了FormattingConversionService 专门用于整合 Converter 和 Formatter,从而使得两者具有一致的编程体验,对开发者更加友好。

底层接口 TypeConverter

定义类型转换方法的接口,它在Spring 2.0就已经存在。在还没有ConversionService之前,它的类型转换动作均委托给已注册的PropertyEditor来完成。但自3.0之后,这个转换动作可能被PropertyEditor来做,也可能交给ConversionService处理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// @since 2.0
public interface TypeConverter {

  // value:待转换的source源数据
  // requiredType:目标类型targetType
  // methodParam:转换的目标方法参数,主要为了分析泛型类型,可能为null
  // field:目标的反射字段,为了泛型,可能为null
  <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
  <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException;
  <T> T convertIfNecessary(Object value, Class<T> requiredType, Field field) throws TypeMismatchException;

}

它是Spring内部使用类型转换的入口,最终委托给PropertyEditor或者注册到ConversionService里的转换器去完成。它的主要实现有:

  • BeanWrapper接口,就是负责Bean属性读写的接口。其继承的TypeConverter接口,就是负责类型转换的。
  • TypeConverterSupport 类是 TypeConverter 接口的实现类,可以看到,所有类型转换逻辑,都是委托给TypeConverterDelegate 工具类的。

TypeConverterDelegate 类

前文提到,Spring同时存在两大类型转换体系:PropertyEditor体系和ConversionService体系,那么什么时候使用前者,什么时候使用后者呢?为了用户更方便地进行类型转换,Spring 提供了TypeConverterDelegate 类作为工具类,该类作为门面类,屏蔽了两大转换体系的使用细节,提供简单的对外接口。

Spring 是优先使用 PropertyEditor 体系进行类型转换的,其次才是 ConversionService 体系。

Spring Boot 扩展

Spring Boot 在内建转换器的基础上额外扩展了不少实用转换器,形如:

  • StringToFileConverter:String -> File
  • NumberToDurationConverter
  • DelimitedStringToCollectionConverter

总结

TypeConverter 是委托 TypeConverterDelegate 工具类完成转换逻辑的,TypeConverterDelegate 工具类又是委托 PropertyEditor 体系和 ConversionService 体系完成转换逻辑的。

有一点需要注意的是,TypeConverterDelegate 类并不直接与 ConversionService 关联,而是通过 PropertyEditorRegistrySupport 间接关联。这样做的目的是为了可配置,毕竟 TypeConverterDelegate 类仅仅是一个工具类,不适合可配置。