成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring的Registrar倒排思想送給你

開發 前端
本文重點是想經由FormatterRegistry注冊中心,引述出Spring常用的Registrar注冊員設計思想,它是一種面向對象編程思想的體現,是不是比面向過程優雅很多呢?

 [[381742]]

本文轉載自微信公眾號「BAT的烏托邦」,作者YourBatman 。轉載本文請聯系BAT的烏托邦公眾號。  

請人吃飯不如請人出汗,請人出汗不如送人以漁。A哥春節繼續營業,這個時候還能看得下去這種技術文章的同學我猜有三類:

  • 要么孤獨了
  • 要么喝醉了
  • 要么喝醉后覺得孤獨了

現實情況往往挺扎心,所以牢記使命,砥礪前行是個好辦法。

上篇文章 把@DateTimeFormat和@NumberFormat注解的實現原理搞清楚了,通過面向元數據編程屏蔽了理解層面、實施層面上的差異化。同時,通過手敲代碼案例,扎扎實實、徹徹底底搞明白了@DateTimeFormat等注解有何用以及如何用,從此不再虛。

像AnnotationFormatterFactory、xxxConverter這種均屬于low-level底層API,上手起來一般頗具難度。一個良好的、流行的框架最起碼應該是上手簡單的,所以開發者應該是最多關心到FormattingConversionService/ConversionService層面即止。本文帶你看看Spring是如何做到醬紫的~

本文提綱

 

版本約定

  • Spring Framework:5.3.x
  • Spring Boot:2.4.x

正文

上文是通過手動調用API的方式實現元數據的解析從而達到數據格式化(轉換)的目的,而在實際應用場景中,作為業務開發者是不可能去直接去操縱API的,畢竟說到底那對開發者太不友好,使用門檻過高。

因此,本文將介紹的是一種更為“高級”的使用方案,看看Spring是如何做到兼具高擴展性的整合,從而對開發者十分友好,相信這便也是Spring最有魅力的地方,一起來學習學習吧。

FormatterRegistry:注冊中心

對于多組件的管理,注冊中心是個很好的解決方案。

FormatterRegistry其實在:9. 細節見真章,Formatter注冊中心的設計很討巧 這篇文章已經有過很詳細的分析,學到了它那非常巧妙的設計,這里也順道推薦你花幾分鐘前往看看。在這篇文章的末尾,A哥故意留下了一個小尾巴沒講:注冊中心對注解工廠AnnotationFormatterFactory的支持,也就是這個接口方法:

  1. FormatterRegistry: 
  2.  
  3.  void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> 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代表的是格式化器注冊員接口,接口定義:

  1. public interface FormatterRegistrar { 
  2.  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等日期時間類型似乎是可以互轉的。

  1. @Test 
  2. public void test1() { 
  3.     FormattingConversionService conversionService = new FormattingConversionService(); 
  4.     // 注冊員負責添加格式化器以支持Date系列的轉換 
  5.     new DateFormatterRegistrar().registerFormatters((FormatterRegistry) conversionService); 
  6.  
  7.     // 1、普通使用 
  8.     long currMills = System.currentTimeMillis(); 
  9.     System.out.println("當前時間戳:" + currMills); 
  10.     // Date -> Calendar 
  11.     System.out.println(conversionService.convert(new Date(currMills), Calendar.class)); 
  12.     // Long ->  Date 
  13.     System.out.println(conversionService.convert(currMills, Date.class)); 
  14.     // Calendar -> Long 
  15.     Calendar calendar = Calendar.getInstance(TimeZone.getDefault()); 
  16.     calendar.setTimeInMillis(currMills); 
  17.     System.out.println(conversionService.convert(calendar, Long.class)); 

運行程序,輸出:

  1. 當前時間戳:1612741385457 
  2. java.util.GregorianCalendar[time=1612741385457 ... 
  3. Mon Feb 08 07:43:05 CST 2021 
  4. 1612741385457 

完美。

注解使用方式

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

  1. // 準備一個Java Bean: 
  2. @Data 
  3. @AllArgsConstructor 
  4. class Son { 
  5.  
  6.     @DateTimeFormat(iso = DateTimeFormat.ISO.DATE
  7.     private Date birthday; 
  8.  

測試代碼:

  1. @Test 
  2. public void test1() { 
  3.     FormattingConversionService conversionService = new FormattingConversionService(); 
  4.     // 重要:重要:重要:注冊基礎的轉換能力 
  5.     DefaultConversionService.addDefaultConverters((ConverterRegistry) conversionService); 
  6.     // 注冊員負責添加格式化器以支持Date系列的轉換 
  7.     new DateFormatterRegistrar().registerFormatters((FormatterRegistry) conversionService); 
  8.  
  9.     // 1、注解使用 
  10.     Son son = new Son(new Date()); 
  11.     // 輸出:將Date類型輸出為Long類型 
  12.     System.out.println(conversionService.convert(son.getBirthday(), Long.class)); 
  13.     // 輸出:將String烈性輸入為Date類型 
  14.     // System.out.println(conversionService.convert("2021-02-12"Date.class)); // 報錯 
  15.     System.out.println(conversionService.convert(1613034123709L, Date.class)); 

運行程序,輸出:

  1. 1613034230018 
  2. Thu Feb 11 17:02:03 CST 2021 

完美。實現了Long類型 <-> Date類型的互轉。

可能有同學會問了,為毛"2021-02-12"就不能convert到Date類型呢?這個原因,額,嗯,哼,若你看了上篇文章 的話,這將不會是個問題。

當然,在實際使用中,更多的情況是String -> Date的轉換case,怎么破?有兩個辦法:

回味本系列前面文章,因為前面有講了不止一次

關注后面文章。因為此case過于常見,后面(特別是在Spring MVC下使用)依舊會重點提及

總結

本文重點是想經由FormatterRegistry注冊中心,引述出Spring常用的Registrar注冊員設計思想,它是一種面向對象編程思想的體現,是不是比面向過程優雅很多呢?本文以DateTimeFormatterRegistrar為示例進行了打樣,可以看到Spring在API抽象這塊著實是非常優秀的,擴展性和方便性兼具,這個度把握得絕佳,或許這也算是設計美學吧。

責任編輯:武曉燕 來源: BAT的烏托邦
相關推薦

2019-02-11 11:16:13

2018-06-16 08:35:57

UnixLinux命令

2018-05-04 09:14:09

Git技巧shell命令

2020-11-19 15:14:30

APP網站軟件

2021-04-15 11:37:47

NumpyPython代碼

2021-09-12 17:23:57

canvas動畫函數

2017-06-05 12:06:00

2017-12-22 09:59:43

2020-06-23 11:49:08

神經網絡數據圖形

2017-10-23 16:28:33

2020-08-06 07:54:24

SpringBoot 圖片識別

2020-07-08 15:30:29

Java面試題代碼

2017-08-28 21:02:55

深度學習神經網絡

2020-11-24 11:30:51

SpringJava代碼

2019-05-06 10:19:31

服務高可用部署

2024-04-15 08:17:21

Spring依賴注入循環依賴

2020-12-24 08:07:18

SpringBootSpring SecuWeb

2025-04-29 08:21:30

ANRAndroidUI

2018-10-10 09:30:29

Spring Boot知識框架
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线不卡一区 | 欧美日韩成人在线 | 国产精品自产拍 | 精品国产91亚洲一区二区三区www | 久久精品欧美一区二区三区不卡 | 91在线资源 | 亚洲精品久久久久久久不卡四虎 | 亚洲啊v在线 | 成人免费视频网站 | 香蕉大人久久国产成人av | www.欧美.com| 91国产精品 | 欧美午夜一区 | 国产精品av久久久久久久久久 | 中文字幕一区二区三区四区五区 | 成人做爰www免费看 午夜精品久久久久久久久久久久 | 日韩在线视频一区二区三区 | 成人午夜精品一区二区三区 | 精品粉嫩aⅴ一区二区三区四区 | 国产一区二区精品在线 | 亚洲国产精品一区二区三区 | 久久一区二区三区四区 | 国产欧美在线一区 | 成av在线 | 久久国产成人 | 国产视频一区二区 | 国产精品一区二区三区在线 | 亚洲乱码国产乱码精品精的特点 | 懂色中文一区二区三区在线视频 | 国产99精品 | 亚洲成人自拍 | 国产精品久久久久久久久久久新郎 | 激情av在线 | 精品1区| 中文字幕一区二区三区四区 | 久久久久久久久久久高潮一区二区 | 澳门永久av免费网站 | 日韩精品一区二区在线 | 亚洲a视频 | 精品一区二区三区在线视频 | 日韩在线一区二区三区 |