spring類型轉(zhuǎn)換器(三)
格式化Formatter
Converter用來(lái)將源數(shù)據(jù)類型轉(zhuǎn)換目標(biāo)數(shù)據(jù)類型,不過(guò)有時(shí)候一個(gè)數(shù)據(jù)類型會(huì)對(duì)應(yīng)不同格式的字符串,如日期類型在不同國(guó)家顯示的字符是不一樣的称勋,需要根據(jù)Locale進(jìn)行轉(zhuǎn)換藐翎,或者需要將日期類型轉(zhuǎn)換為不同格式化的字符串,spring針對(duì)這種情況提供了Formatter接口來(lái)針對(duì)格式化進(jìn)行處理紫岩。
格式化Formatter其實(shí)也是一種Converter规惰,只是兩個(gè)互轉(zhuǎn)類型之間,有一個(gè)固定是String類型泉蝌,F(xiàn)ormatter實(shí)現(xiàn)了不同格式的String類型與其他類型的類型互轉(zhuǎn)
Formatter主要針對(duì)的是Number類型和日期類型歇万。Spring對(duì)這兩種類型的子類和字符串之間的轉(zhuǎn)換提供了格式化實(shí)現(xiàn)揩晴,下面以日期類型的轉(zhuǎn)換為例說(shuō)明。
例子
public static void main(String[] args) throws NoSuchFieldException {
String today = "2018-12-04 12:21:32";
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
Field longDateField = Person.class.getDeclaredField("longDate");
Date longDate = (Date) conversionService.convert(today, new TypeDescriptor(longDateField));
Field shortDateField = Person.class.getDeclaredField("shortDate");
Date shortDate = (Date) conversionService.convert(today, new TypeDescriptor(shortDateField));
Field localDateTimeField = Person.class.getDeclaredField("localDateTime");
LocalDateTime localDateTime = (LocalDateTime) conversionService.convert(today, new TypeDescriptor(localDateTimeField));
Field dateFormatField = Person.class.getDeclaredField("dateFormat");
//日期到字符串的格式化轉(zhuǎn)換贪磺,注意@DateTimeFormat注解一定要加在日期類型上硫兰,加在字符串類型上沒(méi)有用
String dateFormat = (String) conversionService.convert(LocalDateTime.now(), new TypeDescriptor(localDateTimeField), new TypeDescriptor(dateFormatField));
System.out.println(dateFormat);
}
@Data
static class Person{
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date longDate;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date shortDate;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
private String dateFormat;
}
原理分析
- 首先需要明確的一點(diǎn),格式化是完成指定類型和字符串類型的互轉(zhuǎn),那么就可以定性為使用Converter來(lái)完成寒锚,需要注冊(cè)兩個(gè)Converter劫映,String到Class<?>和 Class<?>到String的轉(zhuǎn)換,這樣就可以借用Converter調(diào)用的殼子壕曼,內(nèi)部的轉(zhuǎn)換及格式化再具體實(shí)現(xiàn)
- 格式化功能的實(shí)現(xiàn)是通過(guò)兩個(gè)接口來(lái)實(shí)現(xiàn)苏研,通過(guò)Printer接口
print()
方法實(shí)現(xiàn)指定類型到字符串的轉(zhuǎn)換,通過(guò)Parser接口parse()
方法完成字符串到指定類型的轉(zhuǎn)換腮郊。Formater接口繼承了這兩個(gè)接口 - 格式化可以轉(zhuǎn)換為不同格式的字符串摹蘑,并且可以用戶自定義,那么就需要一個(gè)入口來(lái)讓用戶配置轧飞,所以提供了兩個(gè)注解用于指定字段的格式類型
-
DateTimeFormat
指定日期格式的注解衅鹿,通過(guò)指定pattern或者style實(shí)現(xiàn)指定不同的格式字符串 -
NumberFormat
指定數(shù)字格式的注解,通過(guò)指定pattern或者style實(shí)現(xiàn)指定不同的格式字符串过咬。
-
- 那么通過(guò)什么來(lái)提供注解接入的入口呢大渤,那就是AnnotationFormatterFactory<A extends Annotation>接口,這個(gè)接口集成了注解和Formater接口掸绞,將兩者關(guān)聯(lián)起來(lái)泵三,然后注冊(cè)到Converter中。同時(shí)這個(gè)接口的
getFieldTypes()
方法返回一個(gè)Set<Class<?>>衔掸,可以同時(shí)集成多個(gè)類型到字符串之間的轉(zhuǎn)換烫幕。
源碼
Formatter接口繼承了Printer和Parser接口,一個(gè)用于將對(duì)象格式化為本地化的字符串敞映,一個(gè)將字符串轉(zhuǎn)換為對(duì)象较曼。
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
public interface Printer<T> {
String print(T object, Locale locale);
}
public interface Parser<T> {
T parse(String text, Locale locale) throws ParseException;
}
AnnotationFormatterFactory集成了注解和Formatter≌裨福可以創(chuàng)建帶有Annotation性質(zhì)的Printer和Parser對(duì)象捷犹。
public interface AnnotationFormatterFactory<A extends Annotation> {
//支持多個(gè)類型到字符串之間的轉(zhuǎn)換
Set<Class<?>> getFieldTypes();
//格式化
Printer<?> getPrinter(A annotation, Class<?> fieldType);
//反格式化
Parser<?> getParser(A annotation, Class<?> fieldType);
}
在上章節(jié)的繼承圖中可以看到FormattingConversionService繼承了GenericConversionService。同時(shí)實(shí)現(xiàn)了FormatterRegistry和ConverterRegistry接口
FormatterRegistry擴(kuò)展了ConverterRegistry接口冕末,額外提供了注冊(cè)Formatter萍歉、AnnotationFormatterFactory的功能
public interface FormatterRegistry extends ConverterRegistry {
void addFormatter(Formatter<?> formatter);
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser);
void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory);
}
FormatterRegistrar接口用于批量注冊(cè)Formatter的接口
public interface FormatterRegistrar {
//批量注冊(cè)
void registerFormatters(FormatterRegistry registry);
}
spring提供了FormattingConversionService的默認(rèn)實(shí)現(xiàn)DefaultFormattingConversionService,使用的時(shí)候一般都是直接使用這個(gè)類栓霜。
首先來(lái)看DefaultFormattingConversionService翠桦,在BeanFactory中只接收一個(gè)ConversionService變量,所以只能給spring容易配置一個(gè)ConversionService胳蛮。那么到底應(yīng)該用DefaultFormattingConversionService還是用DefaultConversionService销凑?讓我們來(lái)看DefaultFormattingConversionService中的實(shí)現(xiàn)
public DefaultFormattingConversionService(StringValueResolver embeddedValueResolver, boolean registerDefaultFormatters) {
setEmbeddedValueResolver(embeddedValueResolver);
//注冊(cè)DefaultConversionService中的默認(rèn)轉(zhuǎn)換器
DefaultConversionService.addDefaultConverters(this);
if (registerDefaultFormatters) {
addDefaultFormatters(this);
}
}
從源碼可以看出,DefaultFormattingConversionService完全是對(duì)DefaultConversionService的擴(kuò)展仅炊,在構(gòu)造函數(shù)中調(diào)用了DefaultConversionService的addDefaultConverters完全擁有了DefaultConversionService所有的功能斗幼,所以只需要使用DefaultFormattingConversionService就可以
DefaultFormattingConversionService創(chuàng)建完成之后除了添加Converters,還注冊(cè)了一些Formatters抚垄,這些Formaters主要是spring提供的用于針對(duì)日期類型蜕窿、Number類型、貨幣金額進(jìn)行格式化轉(zhuǎn)換
public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
//支持?jǐn)?shù)字類型的字符串格式化轉(zhuǎn)換
formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
//支持對(duì) 貨幣金額 格式轉(zhuǎn)換
if (jsr354Present) {
formatterRegistry.addFormatter(new CurrencyUnitFormatter());
formatterRegistry.addFormatter(new MonetaryAmountFormatter());
formatterRegistry.addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
}
if (jsr310Present) {
//對(duì)java8的日期類型 字符串格式化轉(zhuǎn)換 LocalDateTime
new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);
}
if (jodaTimePresent) {
//對(duì)joda格式的日期 字符串格式化轉(zhuǎn)換
new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
} else {
//對(duì)普通的Date 呆馁、Calendar桐经、時(shí)間戳 字符串格式化轉(zhuǎn)換
new DateFormatterRegistrar().registerFormatters(formatterRegistry);
}
}
Formater的注冊(cè)和轉(zhuǎn)換
注冊(cè)Formatter
接下來(lái)分析下Formater的注冊(cè)功能。通過(guò)上述分析我們可以知道浙滤,F(xiàn)ormater實(shí)質(zhì)上是Class<?>和String之間的互轉(zhuǎn)阴挣,所以在注冊(cè)的時(shí)候,只需要提供Class<?>纺腊、Printer和Parser畔咧。來(lái)看FormattingConversionService類中的實(shí)現(xiàn)
//沒(méi)有指定Class<?> 直接使用 Formater上的泛型作為和String互轉(zhuǎn)的類型
public void addFormatter(Formatter<?> formatter) {
addFormatterForFieldType(getFieldType(formatter), formatter);
}
//formatter同時(shí)繼承了 Printer和Parser接口
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
addConverter(new PrinterConverter(fieldType, formatter, this));
addConverter(new ParserConverter(fieldType, formatter, this));
}
//注冊(cè)Converter
public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
addConverter(new PrinterConverter(fieldType, printer, this));
addConverter(new ParserConverter(fieldType, parser, this));
}
可以看到的是注冊(cè)Formater的過(guò)程,就是注冊(cè)了一對(duì)Converter揖膜。注冊(cè)Converter的過(guò)程已經(jīng)在前文分析過(guò)誓沸,在這里我們來(lái)一起看下PrinterConverter和ParserConverter類。
private static class PrinterConverter implements GenericConverter {
private final Class<?> fieldType;
private final TypeDescriptor printerObjectType;
private final Printer printer;
private final ConversionService conversionService;//this
public PrinterConverter(Class<?> fieldType, Printer<?> printer, ConversionService conversionService) {
this.fieldType = fieldType;
//獲取Printer上設(shè)置的泛型 即Class<?>
this.printerObjectType = TypeDescriptor.valueOf(resolvePrinterObjectType(printer));
this.printer = printer;
this.conversionService = conversionService;//this
}
@Override //fieldType -> String 轉(zhuǎn)換
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(this.fieldType, String.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return "";
}//如果源數(shù)據(jù)類型和Printer支持的不一致壹粟,首先需要進(jìn)行 源數(shù)據(jù)類型 -> Printer支持類型的轉(zhuǎn)換
if (!sourceType.isAssignableTo(this.printerObjectType)) {
source = this.conversionService.convert(source, sourceType, this.printerObjectType);
} //委托Printer 完成到 String類型的轉(zhuǎn)換
return this.printer.print(source, LocaleContextHolder.getLocale());
}
private Class<?> resolvePrinterObjectType(Printer<?> printer) {
return GenericTypeResolver.resolveTypeArgument(printer.getClass(), Printer.class);
}
}
可以看出PrinterConverter類型實(shí)現(xiàn)GenericConverter拜隧,用于實(shí)現(xiàn)傳入類型到字符串的轉(zhuǎn)換,具體的轉(zhuǎn)換功能委托給參數(shù)Printer實(shí)現(xiàn)類對(duì)象實(shí)現(xiàn)趁仙。這里相當(dāng)于是一個(gè)插件類保留洪添。可以通過(guò)Printer實(shí)現(xiàn)任何類型到String類型的轉(zhuǎn)換幸撕。
再來(lái)看ParserConverter
private static class ParserConverter implements GenericConverter {
private final Class<?> fieldType;
private final Parser<?> parser;
private final ConversionService conversionService;
public ParserConverter(Class<?> fieldType, Parser<?> parser, ConversionService conversionService) {
this.fieldType = fieldType;
this.parser = parser;
this.conversionService = conversionService;//this
}
@Override //String -> fieldType 轉(zhuǎn)換
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, this.fieldType));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
String text = (String) source;
if (!StringUtils.hasText(text)) {
return null;
}
Object result;
try { //調(diào)用parser 將String類型轉(zhuǎn)換為 Parser中聲明的泛型
result = this.parser.parse(text, LocaleContextHolder.getLocale());
}
//catch...
if (result == null) {
//throw ...
}
//如果通過(guò)Parser轉(zhuǎn)換后的類型不是目標(biāo)類型薇组,調(diào)用相應(yīng)的類型轉(zhuǎn)換器繼續(xù)轉(zhuǎn)換
TypeDescriptor resultType = TypeDescriptor.valueOf(result.getClass());
if (!resultType.isAssignableTo(targetType)) {
result = this.conversionService.convert(result, resultType, targetType);
}
return result;
}
}
ParserConverter跟PrinterConverter實(shí)現(xiàn)了一個(gè)反方向的轉(zhuǎn)換,至此注冊(cè)通過(guò)一個(gè)Formater接口實(shí)現(xiàn)類坐儿,就可以完成Formater實(shí)現(xiàn)類中泛型到String類型之間的互轉(zhuǎn)律胀。
例如注冊(cè) LocalDateTime的轉(zhuǎn)換器,實(shí)現(xiàn)LocalDateTime和String類型的互轉(zhuǎn)
registry.addFormatterForFieldType(LocalDateTime.class, new TemporalAccessorPrinter(
dtf == DateTimeFormatter.ISO_DATE_TIME ? DateTimeFormatter.ISO_LOCAL_DATE_TIME : dtf),
new TemporalAccessorParser(LocalDateTime.class, dtf));
可以看到使用一個(gè)類TemporalAccessorPrinter和TemporalAccessorParser貌矿,并都傳入了一個(gè)參數(shù)DateTimeFormatter炭菌,眾所周知LocalDateTime和String格式化就是通過(guò)DateTimeFormatter來(lái)實(shí)現(xiàn)。那么我們猜測(cè)TemporalAccessorParser其實(shí)就是對(duì)DateTimeFormatter的封裝調(diào)用
public final class TemporalAccessorPrinter implements Printer<TemporalAccessor> {
private final DateTimeFormatter formatter;
public TemporalAccessorPrinter(DateTimeFormatter formatter) {
this.formatter = formatter;
}
@Override
public String print(TemporalAccessor partial, Locale locale) {
//調(diào)用formater的format轉(zhuǎn)換為String
return DateTimeContextHolder.getFormatter(this.formatter, locale).format(partial);
}
}
可以看到TemporalAccessorPrinter就是調(diào)用的DateTimeFormatter完成格式化的逛漫,不過(guò)這里使用到了ThreadLocal黑低,可以先不管,TemporalAccessorParser中同理也會(huì)使用DateTimeFormatter完成String到日期的轉(zhuǎn)換
public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
private final DateTimeFormatter formatter;
//...
@Override
public TemporalAccessor parse(String text, Locale locale) throws ParseException {
DateTimeFormatter formatterToUse = DateTimeContextHolder.getFormatter(this.formatter, locale);
if (LocalDate.class == this.temporalAccessorType) {
return LocalDate.parse(text, formatterToUse);
} else if (LocalTime.class == this.temporalAccessorType) {
return LocalTime.parse(text, formatterToUse);
} else if (LocalDateTime.class == this.temporalAccessorType) {
return LocalDateTime.parse(text, formatterToUse);
}
//...
}
}
格式化轉(zhuǎn)換
Formatter的注冊(cè)最終是注冊(cè)了一對(duì)Converter,所以格式化轉(zhuǎn)換完全就是Converter邏輯的實(shí)現(xiàn)克握,在前文已經(jīng)分析過(guò)了蕾管,這里就不再贅述。
AnnotationFormatterFactory的注冊(cè)和轉(zhuǎn)換
注冊(cè)
分析完了Formatter的注冊(cè)和轉(zhuǎn)換過(guò)程菩暗,一起來(lái)看下FormatConversionService提供了另外一種注冊(cè)掰曾。前文提到了可以通過(guò)在對(duì)象字段上聲明一個(gè)注解,在注解中指定格式化后字符串格式停团。這個(gè)功能就是通過(guò)AnnotationFormatterFactory來(lái)實(shí)現(xiàn)的旷坦。來(lái)看FormatConversionService的addFormatterForFieldAnnotation()
方法
@Override
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory) {
//獲取注冊(cè)AnnotationFormatterFactory上的注解
Class<? extends Annotation> annotationType =getAnnotationType(annotationFormatterFactory);
if (this.embeddedValueResolver != null && annotationFormatterFactory instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) annotationFormatterFactory).setEmbeddedValueResolver(this.embeddedValueResolver);
}
//AnnotationFormatterFactory中定義的支持轉(zhuǎn)換的Class<?>集合
Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes();
//該集合中所有的類型都需要完成到String的互轉(zhuǎn),所以循環(huán)封裝Converter并注冊(cè)
for (Class<?> fieldType : fieldTypes) {
//注冊(cè) fieldType --> String 的類型轉(zhuǎn)換器
addConverter(new AnnotationPrinterConverter(annotationType, annotationFormatterFactory, fieldType));
//注冊(cè) String --> fieldType 的類型轉(zhuǎn)換器
addConverter(new AnnotationParserConverter(annotationType, annotationFormatterFactory, fieldType));
}
}
AnnotationFormatterFactory的注冊(cè)佑稠,首先需要獲取的就是AnnotationFormatterFactory泛型中的注解類型秒梅。然后通過(guò)getFieldTypes()
獲取所有聲明的可以進(jìn)行轉(zhuǎn)換類型集合,然后循環(huán)注冊(cè)了兩個(gè)類型轉(zhuǎn)換器AnnotationPrinterConverter和AnnotationParserConverter舌胶。那么來(lái)看一下AnnotationPrinterConverter到底如何完成可配置的類型轉(zhuǎn)換的
private class AnnotationPrinterConverter implements ConditionalGenericConverter {
private final Class<? extends Annotation> annotationType; //注解類型
private final AnnotationFormatterFactory annotationFormatterFactory;
private final Class<?> fieldType;
public AnnotationPrinterConverter(Class<? extends Annotation> annotationType,
AnnotationFormatterFactory<?> annotationFormatterFactory, Class<?> fieldType) {
this.annotationType = annotationType;
this.annotationFormatterFactory = annotationFormatterFactory;
this.fieldType = fieldType;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
//聲明為 fieldType -> String 之間的轉(zhuǎn)換
return Collections.singleton(new ConvertiblePair(this.fieldType, String.class));
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
//轉(zhuǎn)換器生效的條件時(shí) 源數(shù)據(jù)類型上有該注解
return sourceType.hasAnnotation(this.annotationType);
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { //獲取源數(shù)據(jù)類型上的注解
Annotation ann = sourceType.getAnnotation(this.annotationType);
if (ann == null) { //使用該轉(zhuǎn)換器的前提是添加了相同的注解
throw new IllegalStateException("");
}
AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, sourceType.getObjectType());
//將創(chuàng)建PrinterConverter緩存起來(lái)捆蜀,避免每次重復(fù)創(chuàng)建
GenericConverter converter = cachedPrinters.get(converterKey);
if (converter == null) {
//從annotationFormatterFactory中獲取Printer
Printer<?> printer = this.annotationFormatterFactory.getPrinter(
converterKey.getAnnotation(), converterKey.getFieldType());
//在這個(gè)converter中又創(chuàng)建了PrinterConverter
converter = new PrinterConverter(this.fieldType, printer, FormattingConversionService.this);
cachedPrinters.put(converterKey, converter);
}
//使用PrinterConverter 進(jìn)行類型轉(zhuǎn)換
return converter.convert(source, sourceType, targetType);
}
}
可以看到的是,AnnotationPrinterConverter實(shí)現(xiàn)了ConditionalGenericConverter接口辆琅,在matches()
方法中聲明了該轉(zhuǎn)換器只會(huì)作用于Class<?>上有指定的注解的類型漱办。在convert方法中最終創(chuàng)建了一個(gè)PrinterConverter對(duì)象,使用PrinterConverter完成格式化的功能婉烟,這個(gè)在上面已經(jīng)分析過(guò)了娩井。唯一的不同就是Printer的獲取。使用annotationFormatterFactory獲取printer似袁,并將注解作為參數(shù)傳遞進(jìn)去洞辣。所以可以我們可以實(shí)現(xiàn)AnnotationFormatterFactory的getPrinter()
方法提供轉(zhuǎn)換為字符串的功能即可,通過(guò)看以通過(guò)參數(shù)annotation獲取用戶配置的格式昙衅。實(shí)現(xiàn)格式的可配置扬霜。
同樣在AnnotationParserConverter中實(shí)現(xiàn)String到Class<?>的轉(zhuǎn)換,調(diào)用AnnotationFormatterFactory獲取Parser然后創(chuàng)建一個(gè)ParserConverter來(lái)實(shí)現(xiàn)類型轉(zhuǎn)化而涉。需要注意的是 注解只能添加在非String類型那一方上著瓶。
private class AnnotationParserConverter implements ConditionalGenericConverter {
private final Class<? extends Annotation> annotationType;
private final AnnotationFormatterFactory annotationFormatterFactory;
private final Class<?> fieldType;
//此處省略...
@Override//String 到fieldType的轉(zhuǎn)換
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, fieldType));
}
@Override //注意這里用的是targetType 還是要非String類上聲明注解
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return targetType.hasAnnotation(this.annotationType);
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Annotation ann = targetType.getAnnotation(this.annotationType);
if (ann == null) {
//throw ...
}
AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, targetType.getObjectType());
GenericConverter converter = cachedParsers.get(converterKey);
if (converter == null) {
//獲取parser
Parser<?> parser = this.annotationFormatterFactory.getParser(
converterKey.getAnnotation(), converterKey.getFieldType());
//創(chuàng)建ParserConverter進(jìn)行轉(zhuǎn)換
converter = new ParserConverter(this.fieldType, parser, FormattingConversionService.this);
cachedParsers.put(converterKey, converter);
}
return converter.convert(source, sourceType, targetType);
}
}
通過(guò)封裝封裝注冊(cè)AnnotationPrinterConverter和AnnotationParserConverter,用戶需要做的就只有是實(shí)現(xiàn)AnnotationFormatterFactory啼县,在泛型中指定注解材原,然后實(shí)現(xiàn)了getPrinter()
和getParser()
來(lái)實(shí)現(xiàn)Class<?>和String之間的轉(zhuǎn)換,其他的調(diào)用直接走Converter流程季眷。來(lái)看Date類型格式化的實(shí)現(xiàn)
在DateFormatterRegistrar中余蟹,在注冊(cè)Formatters之前,先注冊(cè)了日期類型的一些converter子刮,這里先不去管這個(gè)威酒。最主要的是addFormatterForFieldAnnotation
方法,通過(guò)這個(gè)方法完成對(duì)Formater的注冊(cè)
@Override
public void registerFormatters(FormatterRegistry registry) {
addDateConverters(registry);
//注冊(cè)Formater FormatterFactory
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
if (this.dateFormatter != null) {
//添加對(duì)非注解 Date類型轉(zhuǎn)換
registry.addFormatter(this.dateFormatter);
registry.addFormatterForFieldType(Calendar.class, this.dateFormatter);
}
}
來(lái)看下DateTimeFormatAnnotationFormatterFactory實(shí)現(xiàn)了AnnotationFormatterFactory。并指定注解為DateTimeFormat葵孤。
public class DateTimeFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport
implements AnnotationFormatterFactory<DateTimeFormat> {
private static final Set<Class<?>> FIELD_TYPES;
static {//定義支持的 格式化的類型担钮,支持格式化 Date、Calendar佛呻、Long類型
Set<Class<?>> fieldTypes = new HashSet<Class<?>>(4);
fieldTypes.add(Date.class);
fieldTypes.add(Calendar.class);
fieldTypes.add(Long.class);
FIELD_TYPES = Collections.unmodifiableSet(fieldTypes);
}
@Override //定義支持的 格式化的類型裳朋,支持格式化 Date病线、Calendar吓著、Long類型
public Set<Class<?>> getFieldTypes() {
return FIELD_TYPES;
}
@Override
public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
return getFormatter(annotation, fieldType);
}
@Override
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
return getFormatter(annotation, fieldType);
}
protected Formatter<Date> getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
DateFormatter formatter = new DateFormatter();
//獲取用戶注解中寫(xiě)的格式
formatter.setStylePattern(resolveEmbeddedValue(annotation.style()));
formatter.setIso(annotation.iso()); //獲取用戶注解中寫(xiě)的格式
formatter.setPattern(resolveEmbeddedValue(annotation.pattern())); //獲取用戶注解中寫(xiě)的格式
return formatter;
}
}
可以看到的是在getFieldType()
聲明了支持轉(zhuǎn)換的有Date、Calendar送挑、Long绑莺。可以看到getPrinter()
和getFormatter()
方法返回了一個(gè)DateFormatter對(duì)象惕耕,并將用戶配置的注解信息注入到這個(gè)DateFormatter對(duì)象中
接下來(lái)就是最終的格式化實(shí)現(xiàn)就是這個(gè)DateFormatter對(duì)象了纺裁。大家想一下,一般針對(duì)Date類型的格式化都會(huì)用什么呢司澎?想必大家都猜到了欺缘,沒(méi)錯(cuò)就是SimpleDateFormat。在DateFormatter中就是創(chuàng)建了一個(gè)SimpleDateFormat來(lái)實(shí)現(xiàn)類型和字符串的轉(zhuǎn)換的
public class DateFormatter implements Formatter<Date> {
//....此處省略
@Override
public String print(Date date, Locale locale) {
return getDateFormat(locale).format(date);
}
@Override
public Date parse(String text, Locale locale) throws ParseException {
return getDateFormat(locale).parse(text);
}
protected DateFormat getDateFormat(Locale locale) {
DateFormat dateFormat = createDateFormat(locale);
if (this.timeZone != null) {
dateFormat.setTimeZone(this.timeZone);
}
dateFormat.setLenient(this.lenient);
return dateFormat;
}
//創(chuàng)建一個(gè)SimpleDateFormat,并使用 annotation傳入的pattern
private DateFormat createDateFormat(Locale locale) {
if (StringUtils.hasLength(this.pattern)) {
return new SimpleDateFormat(this.pattern, locale);
}
if (this.iso != null && this.iso != ISO.NONE) {
String pattern = ISO_PATTERNS.get(this.iso);
if (pattern == null) {
throw new IllegalStateException("Unsupported ISO format " + this.iso);
}
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setTimeZone(UTC);
return format;
}
if (StringUtils.hasLength(this.stylePattern)) {
int dateStyle = getStylePatternForChar(0);
int timeStyle = getStylePatternForChar(1);
if (dateStyle != -1 && timeStyle != -1) {
return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
}
if (dateStyle != -1) {
return DateFormat.getDateInstance(dateStyle, locale);
}
if (timeStyle != -1) {
return DateFormat.getTimeInstance(timeStyle, locale);
}
throw new IllegalStateException("Unsupported style pattern");
}
return DateFormat.getDateInstance(this.style, locale);
}
}
至此就實(shí)現(xiàn)了Formatter的注冊(cè)和使用挤安。那么在spring中是如何使用的呢谚殊?
spring中的使用
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<converters>
<set>
<bean class="example.MyCustomConverter"/>
</set>
</converters>
<formatters>
<set>
<bean class="example.MyCustomFormatters"/>
</set>
</formatters>
<formatterRegistrars>
<set>
</set>
</formatterRegistrars>
</bean>
沒(méi)錯(cuò)就注冊(cè)一個(gè)id為conversionService的FormattingConversionServiceFactoryBean對(duì)象。使用這個(gè)就可以了蛤铜,不需要再注冊(cè)ConversionServiceFactoryBean了嫩絮。