溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Spring的Registrar倒排思想

發(fā)布時(shí)間:2021-10-19 09:44:28 來源:億速云 閱讀:148 作者:iii 欄目:web開發(fā)

這篇文章主要介紹“如何理解Spring的Registrar倒排思想”,在日常操作中,相信很多人在如何理解Spring的Registrar倒排思想問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何理解Spring的Registrar倒排思想”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

本文提綱

如何理解Spring的Registrar倒排思想

版本約定

  • Spring Framework:5.3.x

  • Spring Boot:2.4.x

正文

上文是通過手動調(diào)用API的方式實(shí)現(xiàn)元數(shù)據(jù)的解析從而達(dá)到數(shù)據(jù)格式化(轉(zhuǎn)換)的目的,而在實(shí)際應(yīng)用場景中,作為業(yè)務(wù)開發(fā)者是不可能去直接去操縱API的,畢竟說到底那對開發(fā)者太不友好,使用門檻過高。

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

FormatterRegistry:注冊中心

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

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

FormatterRegistry:   void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory);

現(xiàn)在時(shí)機(jī)成熟,本文就來重點(diǎn)關(guān)照它。

該接口方法的唯一實(shí)現(xiàn)在FormattingConversionService里:

如何理解Spring的Registrar倒排思想

①:從AnnotationFormatterFactory的泛型類型中提取到注解類型。注意:若沒有指定泛型(沒有指定注解類型)就拋出異常②:該工廠類支持的類型們③:對于支持的每個(gè)類型,均注冊一個(gè)Printer/Parser

重點(diǎn)在于步驟③,AnnotationPrinterConverter和AnnotationParserConverter均是一個(gè)ConditionalGenericConverter轉(zhuǎn)換器,底層實(shí)現(xiàn)實(shí)際委托給AnnotationFormatterFactory去完成,所以說對AnnotationFormatterFactory的理解格外的重要,還好上篇文章對它已經(jīng)做了詳盡分析,點(diǎn)擊這里電梯直達(dá)。

下面以AnnotationPrinterConverter為例觀其源碼:

如何理解Spring的Registrar倒排思想

①:該轉(zhuǎn)換器只負(fù)責(zé)將fieldType類型轉(zhuǎn)換為String類型②:只有fieldType上標(biāo)注有指定的這個(gè)注解,此轉(zhuǎn)換器才會生效③:轉(zhuǎn)換邏輯。這種緩存式處理邏輯很是常見,其實(shí)最核心的代碼往往只有一句,本處就是它:this.annotationFormatterFactory.getPrinter(...)。獲取到合適的Printer,然后適配為PrinterConverter從而完成最終的convert轉(zhuǎn)換動作

?說明:PrinterConverter和ParserConverter在本系列前面文章已介紹,相關(guān)內(nèi)容可出門左拐在本系列內(nèi)很容易找到?AnnotationParserConverter的實(shí)現(xiàn)邏輯如出一轍,這里就不再啰嗦了。

FormattingConversionService它實(shí)現(xiàn)了FormatterRegistry接口的所有接口方法,但是它并未提供一些默認(rèn)行為。換句話講:實(shí)現(xiàn)了所有的組件注冊/管理的能力,但并沒有“幫你”注冊任何組件,所以還不具備能夠直接提供服務(wù)的條件,若要使用還需“人工干預(yù)”放些組件進(jìn)去才行。

一般來講,對于這種情況一般在外部再包一層  DefaultXXX來提供默認(rèn)服務(wù)是一種對開發(fā)者十分友好的解決方案,Spring也是這么干的,下面來看看DefaultFormattingConversionService為我們默認(rèn)注冊了哪些基礎(chǔ)組件,提供了哪些能力呢。

DefaultFormattingConversionService

默認(rèn)的格式化器轉(zhuǎn)換服務(wù),該默認(rèn)行為適用于大多數(shù)應(yīng)用程序?qū)Ω袷交?、轉(zhuǎn)換器的需求。

繼承自FormattingConversionService,這個(gè)默認(rèn)行為是為該實(shí)例而設(shè)計(jì)的,但為了方便使用,它對外暴露了其static靜態(tài)方法addDefaultFormatters(),這個(gè)設(shè)計(jì)方式同DefaultConversionService暴露了靜態(tài)方法addDefaultConverters()如出一轍。

默認(rèn)注冊了哪些組件?

對于一個(gè)默認(rèn)的Service服務(wù),最關(guān)心的當(dāng)屬它提供了哪些能力。換句話講:它默認(rèn)幫我們注冊了哪些組件呢?

要回答這個(gè)問題可不能靠“背答案”,方式方法其實(shí)非常的簡單,爬進(jìn)去它的源碼處一看便知:

如何理解Spring的Registrar倒排思想

①:雖然說本類(其實(shí)是父類)實(shí)現(xiàn)了EmbeddedValueResolverAware接口,但構(gòu)造時(shí)依舊可以指定占位符處理器StringValueResolver,當(dāng)然一般情況下傳入null即可②:調(diào)用DefaultConversionService的靜態(tài)方法,把默認(rèn)的轉(zhuǎn)換器們都注冊進(jìn)來。那么,默認(rèn)到底注冊了哪些轉(zhuǎn)換器呢?DefaultConversionService.addDefaultConverters(this)該靜態(tài)方法其實(shí)是本系列前面文章所講的內(nèi)容,這里A哥順道也貼在這吧:

如何理解Spring的Registrar倒排思想

③:若registerDefaultFormatters為true就添加默認(rèn)的格式化器們,一般來講,此值都為true。那么,默認(rèn)到底注冊了哪些格式化器呢?

如何理解Spring的Registrar倒排思想

①:對@NumberFormat注解提供支持,格式化數(shù)字(Currency、數(shù)字、百分?jǐn)?shù)等)

②:對JSR  354錢幣類型javax.money.CurrencyUnit、Monetary等類型提供支持。一般情況下,用不著,所以此part不會被真的注冊

③:對JSR-310日期時(shí)間的格式化提供支持。這里使用到了其專用的注冊器DateTimeFormatterRegistrar統(tǒng)一操作

④、⑤:第4、5步是互斥操作,若有Jota-Time就提供對它的支持而不觸發(fā)java.util.Date的注冊器,否則使用后者注冊器。

注意:你以為④、⑤是真的互斥嗎?難道導(dǎo)入了joda-time的包后java.util.Date相關(guān)模塊就失效了?很明顯不是這樣的,讓你“放心”的地方在于JodaTimeFormatterRegistrar注冊器內(nèi)部包含了java.util.Date格式化器的注冊關(guān)系,因此一切都還得到xxxRegistrar里去看才能揭曉。

總之,DefaultFormattingConversionService作為默認(rèn)的格式化轉(zhuǎn)換服務(wù),它是DefaultConversionService的超集,在其基礎(chǔ)上擴(kuò)展了格式化器,格式化注解支持等相關(guān)能力。在Spring環(huán)境下,大多數(shù)情況使用都是它而非DefaultConversionService。

如何理解Spring的Registrar倒排思想

現(xiàn)在,對FormatterRegistry類一個(gè)籠統(tǒng)的認(rèn)識,知道它默認(rèn)給注冊了哪些組件,支持哪些功能,但是細(xì)節(jié)部分還不清晰。比如說:支持哪些數(shù)據(jù)類型?支持哪些格式?這些都藏在相應(yīng)的xxxRegistrar里~

FormatterRegistrar:注冊員

registrar:登記員;注冊主任。

xxxRegistrar它是一種“倒排”思想的設(shè)計(jì)體現(xiàn),能達(dá)到高內(nèi)聚的效果。Spring、Spring  Boot慣用的“伎倆”,譬如你隨便一搜就能看能看到很多很多:

如何理解Spring的Registrar倒排思想

FormatterRegistrar代表的是格式化器注冊員接口,接口定義:

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

接口方法含義:將Converter和Formatter注冊進(jìn)FormatterRegistry注冊中心里,至于注冊哪些組件由各子類自行管理和負(fù)責(zé),而非Registry注冊中心主動去編排。這是一種倒排設(shè)計(jì)思想,能夠很好的達(dá)到高內(nèi)聚的目的。

?注意:雖然存在ConverterRegistry和FormatterRegistry兩個(gè)接口,但只有FormatterRegistrar而 沒有  ConverterRegistrar哦?該接口有三個(gè)實(shí)現(xiàn)類:

如何理解Spring的Registrar倒排思想

見名之意,每個(gè)實(shí)現(xiàn)子類都維護(hù)著自己分內(nèi)之事,邊界十分清晰。

DateFormatterRegistrar:Date注冊員

提供對java.util.Date、java.util.Calendar、long類型的日期時(shí)間的注冊支持。

接口方法實(shí)現(xiàn)如下:

如何理解Spring的Registrar倒排思想

①:添加常規(guī)轉(zhuǎn)換器,支持DateToLong、DateToCalendar、LongToCalendar等基礎(chǔ)轉(zhuǎn)換能力②:若有個(gè)性化指定格式化器,那就給Calendar專門使用。當(dāng)然,大多數(shù)情況下并不會這么做,這步邏輯是為了向后兼容性而考慮而已,一般可忽略③:添加@DateTimeFormat注解的解析支持

代碼示例

下面介紹DateFormatterRegistrar注冊員的使用示例。

普通使用方式

最常規(guī)的轉(zhuǎn)換,Date、Long、Calendar等日期時(shí)間類型似乎是可以互轉(zhuǎn)的。

@Test public void test1() {     FormattingConversionService conversionService = new FormattingConversionService();     // 注冊員負(fù)責(zé)添加格式化器以支持Date系列的轉(zhuǎn)換     new DateFormatterRegistrar().registerFormatters((FormatterRegistry) conversionService);      // 1、普通使用     long currMills = System.currentTimeMillis();     System.out.println("當(dāng)前時(shí)間戳:" + 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)); }

運(yùn)行程序,輸出:

當(dāng)前時(shí)間戳:1612741385457 java.util.GregorianCalendar[time=1612741385457 ... Mon Feb 08 07:43:05 CST 2021 1612741385457

完美。

注解使用方式

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

// 準(zhǔn)備一個(gè)Java Bean: @Data @AllArgsConstructor class Son {      @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)     private Date birthday;  }

測試代碼:

@Test public void test1() {     FormattingConversionService conversionService = new FormattingConversionService();     // 重要:重要:重要:注冊基礎(chǔ)的轉(zhuǎn)換能力     DefaultConversionService.addDefaultConverters((ConverterRegistry) conversionService);     // 注冊員負(fù)責(zé)添加格式化器以支持Date系列的轉(zhuǎn)換     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)); // 報(bào)錯(cuò)     System.out.println(conversionService.convert(1613034123709L, Date.class)); }

運(yùn)行程序,輸出:

1613034230018 Thu Feb 11 17:02:03 CST 2021

完美。實(shí)現(xiàn)了Long類型 <-> Date類型的互轉(zhuǎn)。

到此,關(guān)于“如何理解Spring的Registrar倒排思想”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI