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处理。
|
|
它是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 类仅仅是一个工具类,不适合可配置。