@DateTimeFormat的作用有哪些(@DateTimeFormat,spring,配,web开发)

时间:2024-04-29 08:26:57 作者 : 石家庄SEO 分类 : web开发
  • TAG :

本文提纲

@DateTimeFormat的作用有哪些

版本约定

  • Spring Framework:5.3.x

  • Spring Boot:2.4.x

正文

Spring中的转换器、格式化器是整个Spring技术栈体系中非常重要的一份子,是众多高级特性的基础支撑。

作为一个Spring的使用者,也许你工作了好几年都只接触到@DateTimeFormat这个注解才感知到Spring是有格式化能力的;也许你在使用xml配置、Spring MVC时全然不知自动化封装的流程,也就感知不到Converter转换器模块的存在;也许你还一直不确定@DateTimeFormat能标注在哪些类型上,每次使用时都得用谷歌百度一下......

作为一个Spring的开发者,以上不应该再成为问题。而是能说会道,滚瓜烂熟。下面将本文补充内容传递给你,坐稳发车喽。

@DateTimeFormat注解到底做了什么?

不用猜,很多程序员同学知道/使用@DateTimeFormat注解是在Spring MVC场景,甚至只是在此场景:前端传一个日期时间格式的值,后端使用Date/LocalDateTime接收此值时使用。

Request的请求实体形如这样:

@DatapublicclassPerson{@DateTimeFormat(pattern="yyyy-MM-ddHH:mm:ss")privateLocalDateTimearriveTime;}

这么一来,前端传入"2021-03-07 21:00:00"这种格式的字符串就能被自动封装进arriveTime了。

说明:String -> LocalDateTime arriveTime属于Parser功能(也称作输入),此注解在xxx -> String输出时(Printer功能)也会生效的?

使用了@DateTimeFormat这么久,你是否知道它并不属于spring-web/spring-webmvc模块的类,而是属于spring-context:org.springframework.format.annotation.DateTimeFormat。换句话讲:@DateTimeForma它属于基础设施类,并不是只能用于web层,而是可用于所有有需要转换的地方。

通过上篇文章 我们知道了,@DateTimeFormat和@NumberFormat注解的功能底层是依赖于AnnotationFormatterFactory以及格式化器注册中心FormatterRegistry核心API去完成的。那么这个流程是怎样的呢?

可能这么说还是觉得比较抽象,那么我尝试画了一幅流程图,可助你掌握这部分的核心工作原理(执行流程):

@DateTimeFormat的作用有哪些

该流程可释义为:通过格式化器注册中心FormatterRegistry的API向其注册注解工厂AnnotationFormatterFactory以支持格式化注解。但是,底层其实都(为每个FieldType类型)适配为了Converter才注册到FormatterRegistry进去的。换句话讲:FormatterRegistry(其实是ConverterRegistry)底层管理的永远是一些简单的Converter转换器们,这便也符合了越底层越抽象,越上层越具体的设计原则,是一种良好的设计方案。

值得注意:ConverterRegistry管理的底层这些Converter是分为三大类的哟。1:1、1:N、N:N?

向注册中心注册完成后,转换服务就具备了AnnotationFormatterFactory所支持的类型FieldType <-> String互相转换的能力了。当然喽,让其能执行转换动作还有个前提条件是FieldType上必须标注有AnnotationFormatterFactory指定的注解类型才行,这个时候@DateTimeFormat就发挥作用啦。

这么来看,@DateTimeFormat注解自己其实并未做什么,只是纯被当做Field上的一个元数据被用作参与判断、格式化时所需参数的指定,此注解它是面向开发者的。真正做了“很多事”的其实是AnnotationFormatterFactory和FormatterRegistry等底层核心API,它们在初始化阶段就默默全部完成,而这一切(较为复杂)的逻辑对开发者是完全透明的。

JSR 310日期时间注册员

上篇文章 介绍了Spring格式化器倒排思想,其具体体现在FormatterRegistrar接口的设计,上文用“比较古老”的支持java.util.Date类型的DateFormatterRegistrar打了个样,体验了一把倒排设计的好处。

我们知道在Java领域日期时间类型分为三大领域:老Date体系、JSR 310体系、Joda-time体系。这不FormatterRegistrar接口的继承体系三个实现类刚好与之对应:

@DateTimeFormat的作用有哪些

A哥不建议在开发中再以任何理由再使用Date类型,而是用JSR 310取以代之。因此接下来,就看看DateTimeFormatterRegistrar注册员为我们做了哪些事。

DateTimeFormatterRegistrar:JSR 310注册员

Since 4.0。在Spring下使用以支持JSR 310日期时间的格式化/转换。

我们知道,JSR 310对日期时间的格式化其实已经非常完善了,具体都体现在java.time.format.DateTimeFormatter这个Java原生API里。Spring针对于JSR 310日期时间类型格式化只是在DateTimeFormatter的基础上做了简单封装和适配,让它使用起来的姿势尽量和Date/JodaTime保持一致,以便对开发者更加友好,代码结构设计上也能够趋近于统一。

本系列前面文章介绍过的DateTimeFormatterFactory便是对DateTimeFormatter的简单包装,用于生产格式化器实例的工厂。此处的DateTimeFormatterRegistrar就使用它俩来进行一系列注册动作,因此可理解为他是更上层的封装形式。

源码分析

下面从源码下手一探究竟。

@DateTimeFormat的作用有哪些

截图里示例出该实现类支持的类型,这里用自定义的枚举类来更抽象的方式定义为三类了,即日期、时间、日期时间。这三大类其实包含了JSR 310类型的主要API,包括:LocalDate、LocalTime、LocalDateTime、ZonedDateTime、OffsetDateTime、OffsetTime共计6个API。对比一下这不正就是Jsr310DateTimeFormatAnnotationFormatterFactory所支持的六大类型么,如下截图所示:

@DateTimeFormat的作用有哪些

说明:该份截图是说明@DateTimeFormat只能标注在JSR 310日期时间的这6种类型上才有效哦。

其实,在任何时候Spring都不建议你直接使用原生的DateTimeFormatter这个API,而是用其封装过的org.springframework.format.datetime.standard.DateTimeFormatterFactory来获得一个DateTimeFormatter实例,以便使用起来更具统一性和灵活性。

这不DateTimeFormatterRegistrar它就是这么来干的:

@DateTimeFormat的作用有哪些

这是唯一构造器:3个类型对应的DateTimeFormatter均由Spring封装过的DateTimeFormatterFactory工厂来“动态”产生,而非直接绑定。由于DateTimeFormatter被设计为不可变,若初始化时就绑定上,后面将无法做定制化设置。这也是引入DateTimeFormatterFactory来做定制化参数“缓存”的又一作用~

由于使用DateTimeFormatterFactory而并非直接使用DateTimeFormatter,就可以很方便的对不同类型做参数定制化,如下方法们,它们是作用在DateTimeFormatterFactory上的,从而可以确保多个条件共存:

@DateTimeFormat的作用有哪些

当然,最重要的当属对FormatterRegistrar 接口方法 的实现逻辑:

@DateTimeFormat的作用有哪些

①:这个 步骤类似于上文讲述DateFormatterRegistrar时调用其public静态方法addDateConverters(registry),作用为注册基础转换器(如Date -> Calendar,Date -> Long的Converter转换器),从而提供基本的转换能力。值得注意的是:DateTimeConverters.registerConverters(registry)内部调用了DateFormatterRegistrar.addDateConverters(registry),并且额外增加了LocalDate、Calendar、Long、Instant等等的Converter转换器(如ZonedDateTimeToLocalDateConverter、LongToInstantConverter等等),后者是前者的超集。

无独有偶:jodaTime的JodaTimeConverters.registerConverters(registry)内部必然也调用了DateFormatterRegistrar.addDateConverters(registry)喽,感兴趣可自己去瞅瞅确认下?

②:生成每个类型对应的格式化器。简单的讲就是通过DateTimeFormatterFactory创建出对应的格式化器DateTimeFormatter③:这一步的作用在源码中的注释部分解释得很清楚了,这一大段代码的作用是使用ISO_LOCAL_*这种变种格式化器来代替执行,效果是性能提升2倍

?说明:这个做法在前文提到的Jsr310DateTimeFormatAnnotationFormatterFactory里getPrinter()生成格式化器时也被用到了用以成倍提升转换性能?

④:对于不需要特殊提速的类型,注册绑定上专用的格式化器org.springframework.format.Formatter即可。如PeriodFormatter、DurationFormatter等

@DateTimeFormat的作用有哪些

@DateTimeFormat的作用有哪些

⑤:让@DateTimeFormat注解对JSR 310日期时间提供支持。关于格式化注解方面的知识,请向上爬2层楼 or 点击文首/文末推荐链接均可进入文章进行详细了解,加深记忆。

代码示例

下面介绍DateTimeFormatterRegistrar注册员的使用示例,其中包括API使用方式,以及面向注解的使用方式。

API使用方式

此类使用方式一般门槛较高,需要对底层API有较熟了解才能运用自如,一般是需要在Spring基础上做二次开发的小伙伴才会用到,用个简单示例了解一下用法:

@Testpublicvoidtest1(){FormattingConversionServiceconversionService=newFormattingConversionService();//注册员负责添加格式化器以支持Date系列的转换newDateTimeFormatterRegistrar().registerFormatters((FormatterRegistry)conversionService);//1、普通使用(API方式)LocalDateTimenow=LocalDateTime.now();System.out.println("当前时间:"+now);System.out.println("LocalDateTime转为LocalDate:"+conversionService.convert(now,LocalDate.class));System.out.println("LocalDateTime转为LocalTime:"+conversionService.convert(now,LocalTime.class));//时间戳转InstantlongcurrMills=System.currentTimeMillis();System.out.println("当前时间戳:"+currMills);System.out.println("时间戳转Instant:"+conversionService.convert(currMills,Instant.class));}

运行程序,输出:

当前时间:2021-03-07T21:19:39.752LocalDateTime转为LocalDate:2021-03-07LocalDateTime转为LocalTime:21:19:39.752当前时间戳:1615123179763时间戳转Instant:2021-03-07T13:19:39.763Z

完美。

通过这个示例,现在知道为啥前端传个时间戳,后端不用Long而使用Instant也能“接得住”不报错了吧~

注解使用方式

见与Spring MVC整合使用方式章节,详细解释。

JodaTimeFormatterRegistrar:joda-time注册员

@deprecated as of 5.3,请使用Java标准的JSR 310日期时间代替

Tips:JodaDateTimeFormatAnnotationFormatterFactoryy也一样在5.3版本被标记为过期了?

jodaTime曾经乃是绝对的王者,拯救Java日期时间于水火,直到JSR 310体系的出现。同样的那句话送给你:建议不要在(新)项目中以任何理由去使用jodaTime,而是和Date一样完全放弃,使用JSR 310足矣。

说明:现在不建议再使用JodaTime并非卸磨杀驴,而是JSR 310就是jodaTime的作者/组织捐赠给Java的(你看那语法,多像!),所以现在叫功成身退更为恰当?

由于jodaTime不像Date一样有那么重的历史包袱(关键Date还是JDK内置的核心类),并且它和JSR 310一脉相承,因此在可预见的将来它将彻底告别Java舞台,逐渐消亡。所以呢,我个人认为,再去学习jodaTime(包括周边)已再无必要,so此part就暂且略过喽。

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:@DateTimeFormat的作用有哪些的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:Java中的深拷贝和浅拷贝是什么意思下一篇:

23 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18