spring類型轉(zhuǎn)換器(三)

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;
    }

原理分析

  1. 首先需要明確的一點(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)
  2. 格式化功能的實(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è)接口
  3. 格式化可以轉(zhuǎn)換為不同格式的字符串摹蘑,并且可以用戶自定義,那么就需要一個(gè)入口來(lái)讓用戶配置轧飞,所以提供了兩個(gè)注解用于指定字段的格式類型
    • DateTimeFormat 指定日期格式的注解衅鹿,通過(guò)指定pattern或者style實(shí)現(xiàn)指定不同的格式字符串
    • NumberFormat 指定數(shù)字格式的注解,通過(guò)指定pattern或者style實(shí)現(xiàn)指定不同的格式字符串过咬。
  4. 那么通過(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了嫩絮。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市围肥,隨后出現(xiàn)的幾起案子剿干,更是在濱河造成了極大的恐慌,老刑警劉巖穆刻,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件置尔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡氢伟,警方通過(guò)查閱死者的電腦和手機(jī)榜轿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腐芍,“玉大人差导,你說(shuō)我怎么就攤上這事≈碛拢” “怎么了设褐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我助析,道長(zhǎng)犀被,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任外冀,我火速辦了婚禮寡键,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雪隧。我一直安慰自己西轩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布脑沿。 她就那樣靜靜地躺著藕畔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庄拇。 梳的紋絲不亂的頭發(fā)上注服,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音措近,去河邊找鬼溶弟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瞭郑,可吹牛的內(nèi)容都是我干的辜御。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼凰浮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼我抠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起袜茧,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤菜拓,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后笛厦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體纳鼎,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年裳凸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贱鄙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姨谷,死狀恐怖逗宁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梦湘,我是刑警寧澤瞎颗,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布件甥,位于F島的核電站,受9級(jí)特大地震影響哼拔,放射性物質(zhì)發(fā)生泄漏引有。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一倦逐、第九天 我趴在偏房一處隱蔽的房頂上張望譬正。 院中可真熱鬧,春花似錦檬姥、人聲如沸曾我。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)您单。三九已至,卻和暖如春荞雏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背平酿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工凤优, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜈彼。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓筑辨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親幸逆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子棍辕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354