spring类型转换器(三)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring类型转换器(三)相关的知识,希望对你有一定的参考价值。

参考技术A

Converter用来将源数据类型转换目标数据类型,不过有时候一个数据类型会对应不同格式的字符串,如日期类型在不同国家显示的字符是不一样的,需要根据Locale进行转换,或者需要将日期类型转换为不同格式化的字符串,spring针对这种情况提供了Formatter接口来针对格式化进行处理。

格式化Formatter其实也是一种Converter,只是两个互转类型之间, 有一个固定是String类型 ,Formatter实现了不同格式的String类型与其他类型的类型互转

Formatter主要针对的是Number类型和日期类型。Spring对这两种类型的子类和字符串之间的转换提供了格式化实现,下面以日期类型的转换为例说明。

Formatter接口继承了Printer和Parser接口,一个用于将对象格式化为本地化的字符串,一个将字符串转换为对象。

AnnotationFormatterFactory集成了注解和Formatter。可以创建带有Annotation性质的Printer和Parser对象。

在上章节的继承图中可以看到FormattingConversionService继承了GenericConversionService。同时实现了FormatterRegistry和ConverterRegistry接口

FormatterRegistry扩展了ConverterRegistry接口,额外提供了注册Formatter、AnnotationFormatterFactory的功能

FormatterRegistrar接口用于批量注册Formatter的接口

spring提供了FormattingConversionService的默认实现DefaultFormattingConversionService,使用的时候一般都是直接使用这个类。

首先来看DefaultFormattingConversionService,在BeanFactory中只接收一个ConversionService变量,所以只能给spring容易配置一个ConversionService。那么到底应该用DefaultFormattingConversionService还是用DefaultConversionService?让我们来看DefaultFormattingConversionService中的实现

从源码可以看出,DefaultFormattingConversionService完全是对DefaultConversionService的扩展,在构造函数中调用了DefaultConversionService的addDefaultConverters完全拥有了DefaultConversionService所有的功能,所以只需要使用DefaultFormattingConversionService就可以

DefaultFormattingConversionService创建完成之后除了添加Converters,还注册了一些Formatters,这些Formaters主要是spring提供的用于针对 日期类型 Number类型 货币金额 进行格式化转换

接下来分析下Formater的注册功能。通过上述分析我们可以知道,Formater实质上是Class<?>和String之间的互转,所以在注册的时候,只需要提供Class<?>、Printer和Parser。来看FormattingConversionService类中的实现

可以看到的是注册Formater的过程,就是注册了一对Converter。注册Converter的过程已经在前文分析过,在这里我们来一起看下PrinterConverter和ParserConverter类。

可以看出PrinterConverter类型实现GenericConverter,用于实现传入类型到字符串的转换,具体的转换功能委托给参数Printer实现类对象实现。这里相当于是一个插件类保留。可以通过Printer实现任何类型到String类型的转换。

再来看ParserConverter

ParserConverter跟PrinterConverter实现了一个反方向的转换,至此注册通过一个Formater接口实现类,就可以完成Formater实现类中泛型到String类型之间的互转。

例如注册 LocalDateTime的转换器,实现LocalDateTime和String类型的互转

可以看到使用一个类TemporalAccessorPrinter和TemporalAccessorParser,并都传入了一个参数DateTimeFormatter,众所周知LocalDateTime和String格式化就是通过DateTimeFormatter来实现。那么我们猜测TemporalAccessorParser其实就是对DateTimeFormatter的封装调用

可以看到TemporalAccessorPrinter就是调用的DateTimeFormatter完成格式化的,不过这里使用到了ThreadLocal,可以先不管,TemporalAccessorParser中同理也会使用DateTimeFormatter完成String到日期的转换

Formatter的注册最终是注册了一对Converter,所以格式化转换完全就是Converter逻辑的实现,在前文已经分析过了,这里就不再赘述。

分析完了Formatter的注册和转换过程,一起来看下FormatConversionService提供了另外一种注册。前文提到了可以通过在对象字段上声明一个注解,在注解中指定格式化后字符串格式。这个功能就是通过AnnotationFormatterFactory来实现的。来看FormatConversionService的 addFormatterForFieldAnnotation() 方法

AnnotationFormatterFactory的注册,首先需要获取的就是AnnotationFormatterFactory泛型中的注解类型。然后通过 getFieldTypes() 获取所有声明的可以进行转换类型集合,然后循环注册了两个类型转换器AnnotationPrinterConverter和AnnotationParserConverter。那么来看一下AnnotationPrinterConverter到底如何完成可配置的类型转换的

可以看到的是,AnnotationPrinterConverter实现了ConditionalGenericConverter接口,在 matches() 方法中声明了该转换器只会作用于Class<?>上有指定的注解的类型。在convert方法中最终创建了一个PrinterConverter对象,使用PrinterConverter完成格式化的功能,这个在上面已经分析过了。唯一的不同就是Printer的获取。使用annotationFormatterFactory获取printer,并将注解作为参数传递进去。所以可以我们可以实现AnnotationFormatterFactory的 getPrinter() 方法提供转换为字符串的功能即可,通过看以通过参数annotation获取用户配置的格式。实现格式的可配置。

同样在AnnotationParserConverter中实现String到Class<?>的转换,调用AnnotationFormatterFactory获取Parser然后创建一个ParserConverter来实现类型转化。需要注意的是 注解只能添加在非String类型那一方上。

通过封装封装注册AnnotationPrinterConverter和AnnotationParserConverter,用户需要做的就只有是实现AnnotationFormatterFactory,在泛型中指定注解,然后实现了 getPrinter() 和 getParser() 来实现Class<?>和String之间的转换,其他的调用直接走Converter流程。来看Date类型格式化的实现

在DateFormatterRegistrar中,在注册Formatters之前,先注册了日期类型的一些converter,这里先不去管这个。最主要的是 addFormatterForFieldAnnotation 方法,通过这个方法完成对Formater的注册

来看下DateTimeFormatAnnotationFormatterFactory实现了AnnotationFormatterFactory。并指定注解为DateTimeFormat。

可以看到的是在 getFieldType() 声明了支持转换的有Date、Calendar、Long。可以看到 getPrinter() 和 getFormatter() 方法返回了一个DateFormatter对象,并将用户配置的注解信息注入到这个DateFormatter对象中

接下来就是最终的格式化实现就是这个DateFormatter对象了。大家想一下,一般针对Date类型的格式化都会用什么呢?想必大家都猜到了,没错就是SimpleDateFormat。在DateFormatter中就是创建了一个SimpleDateFormat来实现类型和字符串的转换的

至此就实现了Formatter的注册和使用。那么在spring中是如何使用的呢?

没错就注册一个id为conversionService的FormattingConversionServiceFactoryBean对象。使用这个就可以了,不需要再注册ConversionServiceFactoryBean了。

以上是关于spring类型转换器(三)的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC自定义类型转换器Converter参数解析器HandlerMethodArgumentResolver

关于springmvc怎么自动把前台string类型日期字段转换成date类型

又是一年金九银十!javastring类型的日期格式转换

spring类型转换器(一)

Spring--Spring类型转换

Spring--Spring类型转换