千家信息网

如何理解Spring的Registrar倒排思想

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要介绍"如何理解Spring的Registrar倒排思想",在日常操作中,相信很多人在如何理解Spring的Registrar倒排思想问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作
千家信息网最后更新 2026年11月03日如何理解Spring的Registrar倒排思想

这篇文章主要介绍"如何理解Spring的Registrar倒排思想",在日常操作中,相信很多人在如何理解Spring的Registrar倒排思想问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何理解Spring的Registrar倒排思想"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

本文提纲

版本约定

  • Spring Framework:5.3.x

  • Spring Boot:2.4.x

正文

上文是通过手动调用API的方式实现元数据的解析从而达到数据格式化(转换)的目的,而在实际应用场景中,作为业务开发者是不可能去直接去操纵API的,毕竟说到底那对开发者太不友好,使用门槛过高。

因此,本文将介绍的是一种更为"高级"的使用方案,看看Spring是如何做到兼具高扩展性的整合,从而对开发者十分友好,相信这便也是Spring最有魅力的地方,一起来学习学习吧。

FormatterRegistry:注册中心

对于多组件的管理,注册中心是个很好的解决方案。

FormatterRegistry其实在:9. 细节见真章,Formatter注册中心的设计很讨巧 这篇文章已经有过很详细的分析,学到了它那非常巧妙的设计,这里也顺道推荐你花几分钟前往看看。在这篇文章的末尾,A哥故意留下了一个小尾巴没讲:注册中心对注解工厂AnnotationFormatterFactory的支持,也就是这个接口方法:

FormatterRegistry:   void addFormatterForFieldAnnotation(AnnotationFormatterFactory annotationFormatterFactory);

现在时机成熟,本文就来重点关照它。

该接口方法的唯一实现在FormattingConversionService里:

①:从AnnotationFormatterFactory的泛型类型中提取到注解类型。注意:若没有指定泛型(没有指定注解类型)就抛出异常②:该工厂类支持的类型们③:对于支持的每个类型,均注册一个Printer/Parser

重点在于步骤③,AnnotationPrinterConverter和AnnotationParserConverter均是一个ConditionalGenericConverter转换器,底层实现实际委托给AnnotationFormatterFactory去完成,所以说对AnnotationFormatterFactory的理解格外的重要,还好上篇文章对它已经做了详尽分析,点击这里电梯直达。

下面以AnnotationPrinterConverter为例观其源码:

①:该转换器只负责将fieldType类型转换为String类型②:只有fieldType上标注有指定的这个注解,此转换器才会生效③:转换逻辑。这种缓存式处理逻辑很是常见,其实最核心的代码往往只有一句,本处就是它:this.annotationFormatterFactory.getPrinter(...)。获取到合适的Printer,然后适配为PrinterConverter从而完成最终的convert转换动作

❝说明:PrinterConverter和ParserConverter在本系列前面文章已介绍,相关内容可出门左拐在本系列内很容易找到❞AnnotationParserConverter的实现逻辑如出一辙,这里就不再啰嗦了。

FormattingConversionService它实现了FormatterRegistry接口的所有接口方法,但是它并未提供一些默认行为。换句话讲:实现了所有的组件注册/管理的能力,但并没有"帮你"注册任何组件,所以还不具备能够直接提供服务的条件,若要使用还需"人工干预"放些组件进去才行。

一般来讲,对于这种情况一般在外部再包一层 DefaultXXX来提供默认服务是一种对开发者十分友好的解决方案,Spring也是这么干的,下面来看看DefaultFormattingConversionService为我们默认注册了哪些基础组件,提供了哪些能力呢。

DefaultFormattingConversionService

默认的格式化器转换服务,该默认行为适用于大多数应用程序对格式化器、转换器的需求。

继承自FormattingConversionService,这个默认行为是为该实例而设计的,但为了方便使用,它对外暴露了其static静态方法addDefaultFormatters(),这个设计方式同DefaultConversionService暴露了静态方法addDefaultConverters()如出一辙。

默认注册了哪些组件?

对于一个默认的Service服务,最关心的当属它提供了哪些能力。换句话讲:它默认帮我们注册了哪些组件呢?

要回答这个问题可不能靠"背答案",方式方法其实非常的简单,爬进去它的源码处一看便知:

①:虽然说本类(其实是父类)实现了EmbeddedValueResolverAware接口,但构造时依旧可以指定占位符处理器StringValueResolver,当然一般情况下传入null即可②:调用DefaultConversionService的静态方法,把默认的转换器们都注册进来。那么,默认到底注册了哪些转换器呢?DefaultConversionService.addDefaultConverters(this)该静态方法其实是本系列前面文章所讲的内容,这里A哥顺道也贴在这吧:

③:若registerDefaultFormatters为true就添加默认的格式化器们,一般来讲,此值都为true。那么,默认到底注册了哪些格式化器呢?

①:对@NumberFormat注解提供支持,格式化数字(Currency、数字、百分数等)

②:对JSR 354钱币类型javax.money.CurrencyUnit、Monetary等类型提供支持。一般情况下,用不着,所以此part不会被真的注册

③:对JSR-310日期时间的格式化提供支持。这里使用到了其专用的注册器DateTimeFormatterRegistrar统一操作

④、⑤:第4、5步是互斥操作,若有Jota-Time就提供对它的支持而不触发java.util.Date的注册器,否则使用后者注册器。

注意:你以为④、⑤是真的互斥吗?难道导入了joda-time的包后java.util.Date相关模块就失效了?很明显不是这样的,让你"放心"的地方在于JodaTimeFormatterRegistrar注册器内部包含了java.util.Date格式化器的注册关系,因此一切都还得到xxxRegistrar里去看才能揭晓。

总之,DefaultFormattingConversionService作为默认的格式化转换服务,它是DefaultConversionService的超集,在其基础上扩展了格式化器,格式化注解支持等相关能力。在Spring环境下,大多数情况使用都是它而非DefaultConversionService。

现在,对FormatterRegistry类一个笼统的认识,知道它默认给注册了哪些组件,支持哪些功能,但是细节部分还不清晰。比如说:支持哪些数据类型?支持哪些格式?这些都藏在相应的xxxRegistrar里~

FormatterRegistrar:注册员

registrar:登记员;注册主任。

xxxRegistrar它是一种"倒排"思想的设计体现,能达到高内聚的效果。Spring、Spring Boot惯用的"伎俩",譬如你随便一搜就能看能看到很多很多:

FormatterRegistrar代表的是格式化器注册员接口,接口定义:

public interface FormatterRegistrar {  void registerFormatters(FormatterRegistry registry); }

接口方法含义:将Converter和Formatter注册进FormatterRegistry注册中心里,至于注册哪些组件由各子类自行管理和负责,而非Registry注册中心主动去编排。这是一种倒排设计思想,能够很好的达到高内聚的目的。

❝注意:虽然存在ConverterRegistry和FormatterRegistry两个接口,但只有FormatterRegistrar而 没有 ConverterRegistrar哦❞该接口有三个实现类:

见名之意,每个实现子类都维护着自己分内之事,边界十分清晰。

DateFormatterRegistrar:Date注册员

提供对java.util.Date、java.util.Calendar、long类型的日期时间的注册支持。

接口方法实现如下:

①:添加常规转换器,支持DateToLong、DateToCalendar、LongToCalendar等基础转换能力②:若有个性化指定格式化器,那就给Calendar专门使用。当然,大多数情况下并不会这么做,这步逻辑是为了向后兼容性而考虑而已,一般可忽略③:添加@DateTimeFormat注解的解析支持

代码示例

下面介绍DateFormatterRegistrar注册员的使用示例。

普通使用方式

最常规的转换,Date、Long、Calendar等日期时间类型似乎是可以互转的。

@Test public void test1() {     FormattingConversionService conversionService = new FormattingConversionService();     // 注册员负责添加格式化器以支持Date系列的转换     new DateFormatterRegistrar().registerFormatters((FormatterRegistry) conversionService);      // 1、普通使用     long currMills = System.currentTimeMillis();     System.out.println("当前时间戳:" + currMills);     // Date -> Calendar     System.out.println(conversionService.convert(new Date(currMills), Calendar.class));     // Long ->  Date     System.out.println(conversionService.convert(currMills, Date.class));     // Calendar -> Long     Calendar calendar = Calendar.getInstance(TimeZone.getDefault());     calendar.setTimeInMillis(currMills);     System.out.println(conversionService.convert(calendar, Long.class)); }

运行程序,输出:

当前时间戳:1612741385457 java.util.GregorianCalendar[time=1612741385457 ... Mon Feb 08 07:43:05 CST 2021 1612741385457

完美。

注解使用方式

使用更高级的注解方式,如@DateTimeFormat

// 准备一个Java Bean: @Data @AllArgsConstructor class Son {      @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)     private Date birthday;  }

测试代码:

@Test public void test1() {     FormattingConversionService conversionService = new FormattingConversionService();     // 重要:重要:重要:注册基础的转换能力     DefaultConversionService.addDefaultConverters((ConverterRegistry) conversionService);     // 注册员负责添加格式化器以支持Date系列的转换     new DateFormatterRegistrar().registerFormatters((FormatterRegistry) conversionService);      // 1、注解使用     Son son = new Son(new Date());     // 输出:将Date类型输出为Long类型     System.out.println(conversionService.convert(son.getBirthday(), Long.class));     // 输出:将String烈性输入为Date类型     // System.out.println(conversionService.convert("2021-02-12", Date.class)); // 报错     System.out.println(conversionService.convert(1613034123709L, Date.class)); }

运行程序,输出:

1613034230018 Thu Feb 11 17:02:03 CST 2021

完美。实现了Long类型 <-> Date类型的互转。

到此,关于"如何理解Spring的Registrar倒排思想"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

类型 格式 支持 接口 方法 注解 组件 转换器 思想 方式 能力 学习 设计 情况 时间 服务 输出 重要 基础 开发者 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技有限公司经营范围 重庆互联网络科技 中国 科技 互联网 互联网科技排名 世界互联网领先科技成果 顺网科技2021年互联网排名 互联网科技公司需要的职位 广州优美互联网科技 哈尔滨新华互联网科技学院 互联网科技与管理 宝灵互联网科技有限公司 湖南一路畅行互联网科技 互联网科技与大数据专业 成都互联网科技公司工作好不好 中天科技中国工业互联网 同城互联网络科技 10位科技互联网大佬 上海旺乐尼互联网科技有限公司 高科技和互联网的现代国家 每日互联网科技新闻 南京欣网互联网络科技有限公司 广州善班互联网科技 互联网金融监管科技 受益于互联网科技的高速发展 晓花互联网科技有限公司网址 互联网科技大奖都有哪些公司 中国科技创新与世界互联网大会 996高科技互联网公司 富国互联网科技002692 版纳互联网科技选哪家
0