Spring3.1.0實現原理分析(一).類型轉換

     類型轉換可以說是一個框架的基礎性功能,struts2,mybatis等開源軟件都有自己的類型轉換模塊,之所以將類型轉換作爲Spring源碼架構分析系列博客的第一篇,是因爲我覺得這個模塊在Spring衆多模塊中是處於最底層的,它只被其它模塊引用,而不存在對其它模塊的依賴,柿子找軟的捏,我們就拿它祭旗吧~~。

     先上一張鳥瞰圖,總體結構是不是很簡單,TypeConverter是對外提供服務的接口,其內部主要由兩部分組成,分別是類型轉換服務和類型轉換器。



一. 先介紹類型轉換器相關的重要接口

1.  轉換器接口


該接口很簡單,就定義了一個方法,把對象從源類型轉換成目標類型。在Spring3.1.0版本中具體的實現類就很多了,如下面的轉換器用於把字符串轉換成布爾類型.

StringToBooleanConverter implements Converter<String, Boolean> 


2. 轉換器工廠接口 


這個接口的作用是爲了減少轉換器個數,減少冗餘代碼。接口中只定義了一個根據目標類型獲取轉換器的方法, 所獲取的轉換器有一個特點,就是源類型是固定的(S),但是目標類型是可變的(R及R的派生類),也就是說從工廠中獲取的轉換器支持把源類型轉換成多種目標類型設想如果沒有轉換器工廠,對於String->Big、String->Float、 String->Double....,這得開發多少個轉換器啊,並且轉換邏輯又是相似的,使用工廠一個搞定ConverterFactory<String,Number>是不是方便多了。

在Spring3.1.0版本中有4個實現類, "字符轉數值", "數值轉數值","字符串轉換數值", "字符串轉枚舉"。

 a. CharacterToNumberFactory implements ConverterFactory<Character, Number>

 b. NumberToNumberConverterFactory  implements  ConverterFactory<Number, Number>

 c. StringToNumberConverterFactory  implements ConverterFactory<String, Number>

 d. StringToEnumConverterFactory implements ConverterFactory<String, Enum>


3. 格式化接口


格式化接口本身是一個空接口,未定義任何方法,真正定義方法是它的兩個超接口(解析器和打印器)。解析器和打印器屬於特殊的轉換器,可以從兩個方面來理解這個特殊性。第一解析器的源類型和打印器的目標類型是固定不變的字符串類型;第二多用於帶有格式化屬性的類型轉換,哪些數據類型帶有格式化屬性呢,最常見的就是日期和數值,同樣的一個java日期對象,可以使用不同的格式把它轉換成字符串形式。

在Spring3.1.0版本中主要有兩類實現了格式化接口的對象。

a. 數值的格式化,又可細分爲貨幣格式化對象,百分比格式化對象, 普通數值格式化對象.。

b. 日期格式化,對JodaDate有依賴,必須導入該jar包。


4. 基於註解的格式化工廠接口 


這個接口的作用是解析註解對象,從而創建相應的格式化對象。在Spring3.1版本中只有兩個跟格式化相關的註解對象,分別是"@NumberFormat","@DateTimeFormat"。比如"@NumberFormat"有個枚舉類型的屬性Style,當該屬性值爲"CURRENCY"時,格式化工廠就會爲該字段使用貨幣格式化對象進行類型轉換。

下面來分析下接口的三個方法:

 a. 獲取可以被註解的字段類型集合:比如格式化註解@NumberFormat,它可以註解的字段類型有(Short|Integer|Long|Float|Double|BigDecimal|BigInteger),該方法用於校驗被註解的字段類型是否合法。

 b. 獲取打印器 :根據註解對象獲取打印器,比如某個Double類型的字段,被@NumberFormat註解了,默認情況會創建NumberFormatter格式化對象用於該字段的類型轉換。

 c. 獲取解析器 : 同上。

在Spring3.1.0版本中有三個基於註解的格式化工廠對象:

 a. NumberFormatAnnotationFormatterFactory

 b. JodaDateTimeFormatAnnotationFormatterFactory  

 c. NoJodaDateTimeFormatAnnotationFormatterFactory : 這個只能算是佔位置的,用於未引入JodaDate's jar包的情況,無法獲取格式化對象,會直接拋出異常。


5. 通用轉換器接口


上面介紹的4種接口, 各有各的作用,雖然相互所有關聯,但畢竟是4個獨立的接口,並不方便外部調用。對於外部調用而言最好是隻有一個接口,通用轉換器就是一個對外提供服務的門面接口

該接口及派生接口一共定義了三個方法,簡單分析下:

  • 獲取"可轉換類型對"集合: 所謂類型對就是源類型+目標類型,也就是說這個轉換器支持哪幾種類型之間的互轉。
  • 執行類型轉換:  需要強調的是對於直接實現了該接口的轉換器而言,這個方法僅僅實現了部分類型轉換邏輯,最終還是依賴"轉換器接口"、"轉換器工廠接口"、"格式化接口"實現完整轉換邏輯。
  • 是否支持從源類型到目標類型的轉換: 相當於是一個校驗方法。

有了門面接口後,如何使它和其它轉換器接口產生關聯呢,答案是使用適配器。適配器就像雙面膠,一手拉着面子(適配器自身實現了通用轉換器接口),一手拉着裏子(持有其它轉換器對象),從而使兩者產生了關聯。  

請看下圖,通過適配器可以使用統一的接口(即通用轉換器接口)來調用其它轉換器的功能。



二.  接着介紹類型轉換服務的功能和結構

      總的來說轉換服務的作用是管理通用轉換器(注意是通用轉換器)所謂管理具體是指註冊通用轉換器、獲取通用轉換器、刪除通用轉換器。

      轉換服務在初始化時會把Spring所有默認實現的轉換器對象、轉換器工廠對象、格式化對象、基於註解的格式化工廠對象註冊到Map對象中,即把它們緩存起來,我們姑且叫它緩存A。當收到轉換請求時,轉換服務會根據源類型描述符目標類型描述符從緩存A中獲取通用轉換器,然後把通用轉換器添加到緩存B中(也是一個Map對象),爲什麼這麼處理呢? 因爲根據源類型描述符和目標類型描述符獲取轉換器存在一定的性能開銷,比如根據傳入的源類型從緩存A中找不到通用轉換器,Spring會嘗試根據源類型的超類型和超接口再次去緩存A中查找。爲了避免重複的性能開銷,所以要把得到的通用轉換器置入緩存B中。

    有個地方要特別說明下,如果從緩存A中獲取的通用轉換器是“基於註解的解析器工廠適配器”或“基於註解的打印器工廠適配器“,它們可以從源類型描述符目標類型描述符獲取註解對象,然後根據註解對象獲取格式化對象,同樣它們也會把獲得的打印器或解析器置入緩存C或緩存D中,目的也是爲了避免下次再次解析註解對象的性能開銷。

    下面圖是轉換服務接口體系



三. 總結

     基本上把Spring類型轉換模塊的內容講清楚了,好久沒寫作文了,寫作文真的不比寫代碼輕鬆,修改了好幾遍基本算是讓自己滿意了,希望這篇文章能對大家有所幫助吧。

     可能細心的讀者會發現,我還漏了一塊內容,就是“PropertyEditor”,其實我是故意不想引入這塊內容,因爲Spring之所以還保留PropertyEditor更多的是爲了兼容2.5之前的版本,而且如果引入了PropertyEditor會讓整個類型轉換模塊的結構變得不那麼漂亮了。如果你想借鑑Spring的結構去開發自己的類型轉換模塊完全可以不用考PropertyEditor。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章