青年時種下什麼,老年時就收穫什麼。關注公衆號【 BAT的烏托邦】,有Spring技術棧、MyBatis、JVM、中間件等小而美的 原創專欄供以避免費學習。分享、成長,拒絕淺嘗輒止。本文已被 https://www.yourbatman.cn 收錄。
你好,我是YourBatman。html
Spring早在1.0(2004年發佈,2003年孵化中)的時候,就有了類型轉換功能模塊。此模塊存在的必要性沒必要多說,相信每一個同窗均可理解。最初,Spring作類型轉換器是基於Java標準的java.beans.PropertyEditor
這個API去擴展實現的,直到Spring 3.0後才得以出現更好替代方案(Spring 3.0發佈於2009 年12月)。java
提示:文章末尾附有Spring主要版本的發佈時間和以及主要特性,感興趣者可文末查看
雖然說Spring自3.0就提出了更爲靈活、優秀的類型轉換接口/服務,可是早期基於PropertyEditor
實現的轉換器並未廢棄且還在發揮餘熱中,所以本文就針對其早期類型轉換實現作出專文講解。git
說明:版本均於2020-11發佈,且版本號均不帶有
.RELEASE
後綴,這和以前是不同的。具體緣由請參考:
Spring改變版本號命名規則:此舉對非英語國家很友好
若你用當下的眼光去看Spring基於PropertyEditor
的類型轉換實現,會發現這麼搞是存在一些設計缺陷的。固然並不能這麼去看,畢竟如今都2020年了,那會才哪跟哪呢。既然Spring裏的PropertyEditor
現現在依然健在,那咱就會會它唄。程序員
PropertyEditor
位於java.beans包中,要知道這個包裏面的類都是設計爲Java GUI程序(AWT)服務的,因此你看官方javadoc對PropertyEditor
的介紹也無出其右:github
A PropertyEditor class provides support for GUIs that want to allow users to edit a property value of a given type.
爲GUI程序提供支持,容許你對給定的value進行編輯,做用相似於一個轉換器:GUI上你能夠輸入、編輯某個屬性而後通過它轉換成合適的類型。
web
此接口提供的方法挺多的,和本文類型轉換有關的最多隻有4個:spring
void setValue(Object value)
:設置屬性值Object getValue()
:獲取屬性值String getAsText()
:輸出。把屬性值轉換成String輸出void setAsText(String text)
:輸入。將String轉換爲屬性值類型輸入JDK對PropertyEditor接口提供了一個默認實現java.beans.PropertyEditorSupport
,所以咱們若需擴展此接口,僅需繼承此類,根據須要複寫getAsText/setAsText
這兩個方法便可,Spring無一例外都是這麼作的。數據庫
PropertyEditor做爲一個JDK原生接口,內置了一些基本實現來服務於GUI程序,如:json
BooleanEditor
:將true/false字符串轉換爲Boolean類型IntegerEditor
:將字符串轉換爲Integer類型segmentfault
JDK內置的實現比較少(如上),功能簡陋,但對於服務GUI程序來講已經夠用,畢竟界面輸入的只多是字符串,而且還均是基礎類型。但這對於複雜的Spring環境、以及富文本的web環境來講就不夠用了,因此Spring在此基礎上有所擴展,所以纔有了本文來討論。
PropertyEditor
實現的是雙向類型轉換:String和Object互轉。調用setValue()
方法後,須要先「緩存」起來後續纔可以使用(輸出)。PropertyEditorSupport
爲此提供了一個成員屬性來作:
PropertyEditorSupport: // 調用setValue()方法對此屬性賦值 getValue()方法取值 private Object value;
這麼一來PropertyEditorSupport
就是有狀態的了,所以是線程不安全的。在使用過程當中須要特別注意,避免出現併發風險。
說明:Support類裏還看到屬性監聽器
PropertyChangeListener
,因它屬於GUI程序使用的組件,與咱們無關,因此後續絲絕不會說起哦
Spring內置的全部擴展均是基於PropertyEditorSupport來實現的,所以也都是線程不安全的哦~
官方的javadoc都說得很清楚:PropertyEditor設計是爲GUI程序服務的,那麼Spring爲什麼看上它了呢?
試想一下:那會的Spring只能支持xml方式配置,而XML屬於文本類型配置,所以在給某個屬性設定值的時候,書寫上去的100%是個字符串,可是此屬性對應的類型卻不必定是字符串,多是任意類型。你思考下,這種場景是否是跟GUI程序(AWT)一毛同樣:輸入字符串,對應任意類型。
爲了實現這種需求,在PropertyEditorSupport
的基礎上只須要複寫setAsText
和getAsText
這兩個方法就行,而後Spring就這麼幹了。我我的yy
一下,當初Spring選擇這麼幹而沒本身另起爐竈的緣由可能有這麼幾個:
2003年左右,Java GUI程序還並未退出歷史舞臺,Spring爲了通用性就選擇基於它擴展嘍
Spring爲了擴展自身功能,提升配置靈活性,擴展出了很是很是多的PropertyEditor
實現,共計40餘個,部分截圖以下:
PropertyEditor | 功能 | 舉例 |
---|---|---|
ZoneIdEditor | 轉爲java.time.ZoneId | Asia/Shanghai |
URLEditor | 轉爲URL,支持傳統方式file:,http: ,也支持Spring風格:classpath:,context上下文相對路徑 等等 |
http://www.baidu.com |
StringTrimmerEditor | trim()字符串,也可刪除指定字符char | 任意字符串 |
StringArrayPropertyEditor | 轉爲字符串數組 | A,B,C |
PropertiesEditor | 轉爲Properties | name = YourBatman |
PatternEditor | 轉爲Pattern | (\D)(\d+)(.) |
PathEditor | 轉爲java.nio.file.Path。支持傳統URL和Spring風格的url | classpath:xxx |
ClassEditor | 轉爲Class | 全類名 |
CustomBooleanEditor | 轉爲Boolean | 見示例 |
CharsetEditor | 轉爲Charset | 見示例 |
CustomDateEditor | 轉爲java.util.Date | 見示例 |
Spring把實現基本(大多數)都放在org.springframework.beans.propertyeditors
包下,接下來我挑選幾個表明性API舉例說明。
@Test public void test1() { PropertyEditor editor = new CustomBooleanEditor(true); // 這些都是true,不區分大小寫 editor.setAsText("trUe"); System.out.println(editor.getAsText()); editor.setAsText("on"); System.out.println(editor.getAsText()); editor.setAsText("yes"); System.out.println(editor.getAsText()); editor.setAsText("1"); System.out.println(editor.getAsText()); // 這些都是false(注意:null並不會輸出爲false,而是輸出空串) editor.setAsText(null); System.out.println(editor.getAsText()); editor.setAsText("fAlse"); System.out.println(editor.getAsText()); editor.setAsText("off"); System.out.println(editor.getAsText()); editor.setAsText("no"); System.out.println(editor.getAsText()); editor.setAsText("0"); System.out.println(editor.getAsText()); // 報錯 editor.setAsText("2"); System.out.println(editor.getAsText()); }
關注點:對於Spring來講,傳入的true、on、yes、1等都會被「翻譯」成true(字母不區分大小寫),大大提升兼容性。
如今知道爲啥你用Postman傳個1,用Bool值也能正常封裝進去了吧,就是它的功勞。此效果等同於轉換器
StringToBooleanConverter
,將在後面進行講述對比
@Test public void test2() { // 雖然都行,但建議你規範書寫:UTF-8 PropertyEditor editor = new CharsetEditor(); editor.setAsText("UtF-8"); System.out.println(editor.getAsText()); // UTF-8 editor.setAsText("utF8"); System.out.println(editor.getAsText()); // UTF-8 }
關注點:utf-8中間的橫槓可要可不要,但建議加上使用標準寫法,另外字母也是不區分大小寫的。
@Test public void test3() { PropertyEditor editor = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),true); editor.setAsText("2020-11-30 09:10:10"); System.out.println(editor.getAsText()); // 2020-11-30 09:10:10 // null輸出空串 editor.setAsText(null); System.out.println(editor.getAsText()); // 報錯 editor.setAsText("2020-11-30"); System.out.println(editor.getAsText()); }
關注點:這個時間/日期轉換器很很差用,構造時就必須指定一個SimpleDateFormat
格式化器。在實際應用中,Spring並無使用到它,而是用後面會說到的替代方案。
把沒有放在org.springframework.beans.propertyeditors
包下的實現稱做特殊實現(前者稱爲標準實現)。
MediaTypeEditor:位於org.springframework.http。依賴的核心API是MediaType.parseMediaType(text)
,能夠把諸如text/html、application/json轉爲MediaType對象
FormatterPropertyEditorAdapter:位於org.springframework.format.support。將3.0新增的Formatter接口適配爲一個PropertyEditor:setAsText這種轉換操做委託給formatter.parse()
去完成,反向委託給formatter.print()
去完成。
DataBinder#addCustomFormatter()
獲得應用ResourceEditor:位於org.springframework.core.io。此轉換器將String轉換爲Resource
資源,特別實用。做爲基礎設施,普遍被用到Spring的不少地方
FileEditor、InputSourceEditor、InputStreamEditor、URLEditor
等等與資源相關的轉換器,均依賴它而實現@Test public void test4() { // 支持標準URL如file:C:/myfile.txt,也支持classpath:myfile.txt // 同時還支持佔位符形式 PropertyEditor editor = new ResourceEditor(new DefaultResourceLoader(), new StandardEnvironment(), true); // file:形式本處略 // editor.setAsText("file:..."); // System.out.println(editor.getAsText()); // classpath形式(注意:若文件不存在不會報錯,而是輸出null) editor.setAsText("classpath:app.properties"); System.out.println(editor.getAsText()); // 輸出帶盤符的全路徑 System.setProperty("myFile.name", "app.properties"); editor.setAsText("classpath:${myFile.name}"); System.out.println(editor.getAsText()); // 結果同上 }
關注點:Spring擴展出來的Resource不只自持常規file:
資源協議,還支持平時使用最多的classpath:
協議,可謂很是好用。
ConvertingPropertyEditorAdapter:位於org.springframework.core.convert.support。將3.0新增的ConversionService
轉換服務適配爲一個PropertyEditor
,內部轉換動做都委託給前者去完成。
AbstractPropertyBindingResult#findEditor()
爲屬性尋找合適PropertyEditor的時候,若ConversionService能支持就包裝爲ConvertingPropertyEditorAdapter供以使用,這是適配器模式的典型應用場景。考慮到閱讀的溫馨性,單篇文章不宜太長,所以涉及到PropertyEditor
的這幾個問題,放在下篇文章單獨列出。這個幾個問題會明顯比本文更深刻,歡迎保持持續關注,peace!
本文主要介紹了三點內容:
PropertyEditor雖然已經很古老,不適合當下複雜環境。但不能否認它依舊有存在的價值,Spring內部也大量的仍在使用,所以不容忽視。下篇文章將深度探討Spring內部是如何使用PropertyEditor的,賦予了它哪些機制,以及最終爲什麼仍是決定本身另起爐竈搞一套呢?歡迎對本系列保持持續關注~
interface21
,它便就是Spring的前身截止到當前,Spring Framework的最新版本是5.3.x
版本,此版本是5.x的最後一個主要功能分支,下個版本將是6.x嘍,我們拭目以待。
Author | A哥(YourBatman) |
---|---|
我的站點 | www.yourbatman.cn |
yourbatman@qq.com | |
微 信 | fsx1056342982 |
活躍平臺 |
|
公衆號 | BAT的烏托邦(ID:BAT-utopia) |
知識星球 | BAT的烏托邦 |
每日文章推薦 | 每日文章推薦 |