②:數據驗證:須要顯示調用Spring的Validator接口實現進行數據驗證;java
③:格式化顯示:須要調用PropertyEditor的getText進行格式化顯示。web
使用如上架構的缺點是:spring
(一、PropertyEditor被設計爲只能String<——>Object之間轉換,不能任意對象類型<——>任意類型,如咱們常見的Long時間戳到Date類型的轉換是辦不到的;express
(二、PropertyEditor是線程不安全的,也就是有狀態的,所以每次使用時都須要建立一個,不可重用;編程
(三、PropertyEditor不是強類型的,setValue(Object)能夠接受任意類型,所以須要咱們本身判斷類型是否兼容;數組
(四、須要本身編程實現驗證,Spring3支持更棒的註解驗證支持;安全
(五、在使用SpEL表達式語言或DataBinder時,只能進行String<--->Object之間的類型轉換;服務器
(6
、不支持細粒度的類型轉換/格式化,如UserModel的registerDate須要轉換/格式化相似「
2012-05-01
」的數據,而OrderModel的orderDate須要轉換/格式化相似「2012-05-0115:11:13」的數據,由於你們都爲java.util.Date類型,所以不太容易進行細粒度轉換/格式化。
架構
在Spring Web MVC環境中,數據類型轉換、驗證及格式化一般是這樣使用的:
mvc
流程:
①、類型轉換:首先表單數據(所有是字符串)經過WebDataBinder進行綁定到命令對象,內部經過PropertyEditor實現;
②:數據驗證:在控制器中的功能處理方法中,須要顯示的調用Spring的Validator實現並將錯誤信息添加到BindingResult對象中;
③:格式化顯示:在表單頁面能夠經過以下方式展現經過PropertyEditor
格式化的數據和錯誤信息:
首先須要經過如上taglib指令引入spring的兩個標籤庫。
如上PropertyEditor和驗證API使用起來比較麻煩,並且有許多缺點,所以Spring3提供了更強大的類型轉換(Type Conversion)支持,它能夠在任意對象之間進行類型轉換,不只僅是String<——>Object;也提供了強大的數據驗證支持;同時提供了強大的數據格式化支持。
2、從Spring3開始,咱們可使用以下架構進行類型轉換、驗證及格式化:
流程:
①:類型轉換:內部的ConversionService會根據S源類型/T目標類型自動選擇相應的Converter SPI進行類型轉換,並且是強類型的,能在任意類型數據之間進行轉換;
②:數據驗證:支持JSR-303驗證框架,如將@Valid放在須要驗證的目標類型上便可;
③:格式化顯示:其實就是任意目標類型---->String的轉換,徹底可使用Converter SPI完成。
Spring爲了更好的詮釋格式化/解析功能提供了Formatter SPI,支持根據Locale信息進行格式化/解析,並且該套SPI能夠支持字段/參數級別的細粒度格式化/解析,流程以下:
①:類型解析(轉換):String---->T類型目標對象的解析,和PropertyEditor相似;
③:格式化顯示:任意目標類型---->String的轉換,和PropertyEditor相似。
Formatter SPI最大特色是能進行字段/參數級別的細粒度解析/格式化控制,即便是Converter SPI也是粗粒度的(到某個具體類型,而不是其中的某個字段單獨控制),目前Formatter SPI還不是很完善,若是您有好的想法能夠到Spring官網提建議。
Formatter SPI內部實現實際委託給Converter SPI進行轉換,即約束爲解析/格式化String<---->任意目標類型。
在Spring Web MVC環境中,數據類型轉換、驗證及格式化一般是這樣使用的:
①、類型轉換:首先表單數據(所有是字符串)經過WebDataBinder進行綁定到命令對象,內部經過Converter SPI實現;
②:數據驗證:使用JSR-303驗證框架進行驗證;
③:格式化顯示:在表單頁面能夠經過以下方式展現經過內部經過Converter SPI
格式化的數據和錯誤信息:
首先須要經過如上taglib指令引入spring的兩個標籤庫。
如上代碼能工做的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的做用是暴露conversionService到請求中以便如<spring:eval>標籤使用。
接下來咱們就詳細學習一下這些知識吧。
PropertyEditor介紹請參考【4.16.一、數據類型轉換】。
1、測試以前咱們須要準備好測試環境:
(一、模型對象,和【4.16.一、數據類型轉換】使用的同樣,須要將DataBinderTestModel模型類及相關類拷貝過來放入cn.javass.chapter7.model包中。
(二、控制器定義:
(三、Spring配置文件定義,請參考chapter7-servlet.xml,並註冊DataBinderTestController:
(四、測試的URL:
http://localhost:9080/springmvc-chapter7/dataBind?username=zhang&bool=yes&schooInfo.specialty=computer&hobbyList[0]=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&phoneNumber=010-12345678&date=2012-3-18 16:48:48&state=blocked
2、註解式控制器註冊PropertyEditor:
一、使用WebDataBinder進行控制器級別註冊PropertyEditor(控制器獨享)
和【4.16.一、數據類型轉換】一節相似,只是此處須要經過@InitBinder來註冊自定義的PropertyEditor。
二、使用WebBindingInitializer批量註冊
PropertyEditor
和【4.16.一、數據類型轉換】不太同樣,由於咱們的註解式控制器是POJO,沒有實現任何東西,所以沒法注入WebBindingInitializer,此時咱們須要把WebBindingInitializer注入到咱們的RequestMappingHandlerAdapter或AnnotationMethodHandlerAdapter,這樣對於全部的註解式控制器都是共享的。
此時咱們註釋掉控制器級別經過@InitBinder註冊PropertyEditor的方法。
三、全局級別註冊PropertyEditor(全局共享)
和【4.16.一、數據類型轉換】一節同樣,此處再也不重複。請參考【4.16.一、數據類型轉換】的【全局級別註冊PropertyEditor(全局共享)】。
接下來咱們看一下Spring3提供的更強大的類型轉換支持。
Spring3引入了更加通用的類型轉換系統,其定義了SPI接口(Converter等)和相應的運行時執行類型轉換的API(ConversionService等),在Spring中它和PropertyEditor功能相似,能夠替代PropertyEditor來轉換外部Bean屬性的值到Bean屬性須要的類型。
該類型轉換系統是Spring通用的,其定義在org.springframework.core.convert包中,不只僅在Spring Web MVC場景下。目標是徹底替換PropertyEditor,提供無狀態、強類型且能夠在任意類型之間轉換的類型轉換系統,能夠用於任何須要的地方,如SpEL、數據綁定。
Converter SPI完成通用的類型轉換邏輯,如java.util.Date<---->java.lang.Long或java.lang.String---->PhoneNumberModel等。
一、類型轉換器:提供類型轉換的實現支持。
一個有以下三種接口:
(一、Converter:類型轉換器,用於轉換S類型到T類型,此接口的實現必須是線程安全的且能夠被共享。
示例:請參考cn.javass.chapter7.converter.support.StringToPhoneNumberConverter轉換器,用於將String--->PhoneNumberModel。
此處咱們能夠看到Converter接口實現只能轉換一種類型到另外一種類型,不能進行多類型轉換,如將一個數組轉換成集合,如(String[] ----> List<String>、String[]----->List<PhoneNumberModel>等)。
(二、GenericConverter和ConditionalGenericConverter:GenericConverter接口實現能在多種類型之間進行轉換,ConditionalGenericConverter是有條件的在多種類型之間進行轉換。
getConvertibleTypes:指定了能夠轉換的目標類型對;
convert:在sourceType和targetType類型之間進行轉換。
matches:用於判斷sourceType和targetType類型之間可否進行類型轉換。
示例:如org.springframework.core.convert.support.ArrayToCollectionConverter和CollectionToArrayConverter用於在數組和集合間進行轉換的ConditionalGenericConverter實現,如在String[]<---->List<String>、String[]<---->List<PhoneNumberModel>等之間進行類型轉換。
對於咱們大部分用戶來講通常不須要自定義GenericConverter, 若是須要能夠參考內置的GenericConverter來實現本身的。
(三、ConverterFactory:工廠模式的實現,用於選擇將一種S源類型轉換爲R類型的子類型T的轉換器的工廠接口。
S:源類型;R目標類型的父類型;T:目標類型,且是R類型的子類型;
getConverter:獲得目標類型的對應的轉換器。
示例:如org.springframework.core.convert.support.NumberToNumberConverterFactory用於在Number類型子類型之間進行轉換,如Integer--->Double, Byte---->Integer, Float--->Double等。
對於咱們大部分用戶來講通常不須要自定義ConverterFactory,若是須要能夠參考內置的ConverterFactory來實現本身的。
二、類型轉換器註冊器、類型轉換服務:提供類型轉換器註冊支持,運行時類型轉換API支持。
一共有以下兩種接口:
(一、ConverterRegistry:類型轉換器註冊支持,能夠註冊/刪除相應的類型轉換器。
能夠註冊:Converter實現,GenericConverter實現,ConverterFactory實現。
(二、ConversionService:運行時類型轉換服務接口,提供運行期類型轉換的支持。
convert:將源對象轉換爲目標類型的目標對象。
Spring提供了兩個默認實現(其都實現了ConverterRegistry、ConversionService接口):
DefaultConversionService:默認的類型轉換服務實現;
DefaultFormattingConversionService:帶數據格式化支持的類型轉換服務實現,通常使用該服務實現便可。
類名 |
說明 |
第一組:標量轉換器 |
|
StringToBooleanConverter |
String----->Boolean true:true/on/yes/1; false:false/off/no/0 |
ObjectToStringConverter |
Object----->String 調用toString方法轉換 |
StringToNumberConverterFactory |
String----->Number(如Integer、Long等) |
NumberToNumberConverterFactory |
Number子類型(Integer、Long、Double等)<——> Number子類型(Integer、Long、Double等) |
StringToCharacterConverter |
String----->java.lang.Character 取字符串第一個字符 |
NumberToCharacterConverter |
Number子類型(Integer、Long、Double等)——> java.lang.Character |
CharacterToNumberFactory |
java.lang.Character ——>Number子類型(Integer、Long、Double等) |
StringToEnumConverterFactory |
String----->enum類型 經過Enum.valueOf將字符串轉換爲須要的enum類型 |
EnumToStringConverter |
enum類型----->String 返回enum對象的name()值 |
StringToLocaleConverter |
String----->java.util.Local |
PropertiesToStringConverter |
java.util.Properties----->String 默認經過ISO-8859-1解碼 |
StringToPropertiesConverter |
String----->java.util.Properties 默認使用ISO-8859-1編碼 |
第二組:集合、數組相關轉換器 |
|
ArrayToCollectionConverter |
任意S數組---->任意T集合(List、Set) |
CollectionToArrayConverter |
任意T集合(List、Set)---->任意S數組 |
ArrayToArrayConverter |
任意S數組<---->任意T數組 |
CollectionToCollectionConverter |
任意T集合(List、Set)<---->任意T集合(List、Set) 即集合之間的類型轉換 |
MapToMapConverter |
Map<---->Map之間的轉換 |
ArrayToStringConverter |
任意S數組---->String類型 |
StringToArrayConverter |
String----->數組 默認經過「,」分割,且去除字符串的兩邊空格(trim) |
ArrayToObjectConverter |
任意S數組---->任意Object的轉換 (若是目標類型和源類型兼容,直接返回源對象;不然返回S數組的第一個元素並進行類型轉換) |
ObjectToArrayConverter |
Object----->單元素數組 |
CollectionToStringConverter |
任意T集合(List、Set)---->String類型 |
StringToCollectionConverter |
String----->集合(List、Set) 默認經過「,」分割,且去除字符串的兩邊空格(trim) |
CollectionToObjectConverter |
任意T集合---->任意Object的轉換 (若是目標類型和源類型兼容,直接返回源對象;不然返回S數組的第一個元素並進行類型轉換) |
ObjectToCollectionConverter |
Object----->單元素集合 |
第三組:默認(fallback)轉換器:以前的轉換器不能轉換時調用 |
|
ObjectToObjectConverter |
Object(S)----->Object(T) 首先嚐試valueOf進行轉換、沒有則嘗試new 構造器(S) |
IdToEntityConverter |
Id(S)----->Entity(T) 查找並調用public static T find[EntityName](S)獲取目標對象,EntityName是T類型的簡單類型 |
FallbackObjectToStringConverter |
Object----->String ConversionService做爲恢復使用,即其餘轉換器不能轉換時調用(執行對象的toString()方法) |
S:表明源類型,T:表明目標類型
如上的轉換器在使用轉換服務實現DefaultConversionService和DefaultFormattingConversionService時會自動註冊。
(一、自定義String----->PhoneNumberModel的轉換器
String轉換爲Date的類型轉換器,請參考cn.javass.chapter7.web.controller.support.converter.StringToDateConverter。
(二、測試用例(cn.javass.chapter7.web.controller.support.converter.ConverterTest)
相似於PhoneNumberEditor將字符串「010-12345678」轉換爲PhoneNumberModel。
其餘類型轉換器使用也是相似的,此處再也不重複。
(一、註冊ConversionService實現和自定義的類型轉換器
FormattingConversionServiceFactoryBean:是FactoryBean實現,默認使用DefaultFormattingConversionService轉換器服務實現;
converters:註冊咱們自定義的類型轉換器,此處註冊了String--->PhoneNumberModel和String--->Date的類型轉換器。
(二、經過ConfigurableWebBindingInitializer註冊ConversionService
此處咱們經過ConfigurableWebBindingInitializer綁定初始化器進行ConversionService的註冊;
三、註冊ConfigurableWebBindingInitializer到RequestMappingHandlerAdapter
經過如上配置,咱們就完成了Spring3.0的類型轉換系統與Spring Web MVC的集成。此時能夠啓動服務器輸入以前的URL測試了。
此時可能有人會問,若是我同時使用PropertyEditor和ConversionService,執行順序是什麼呢?內部首先查找PropertyEditor進行類型轉換,若是沒有找到相應的PropertyEditor再經過ConversionService進行轉換。
如上集成過程看起來比較麻煩,後邊咱們會介紹<mvc:annotation-driven>和@EnableWebMvc,ConversionService會自動註冊,後續章節再詳細介紹。
②:數據驗證:須要顯示調用Spring的Validator接口實現進行數據驗證;
③:格式化顯示:須要調用PropertyEditor的getText進行格式化顯示。
使用如上架構的缺點是:
(一、PropertyEditor被設計爲只能String<——>Object之間轉換,不能任意對象類型<——>任意類型,如咱們常見的Long時間戳到Date類型的轉換是辦不到的;
(二、PropertyEditor是線程不安全的,也就是有狀態的,所以每次使用時都須要建立一個,不可重用;
(三、PropertyEditor不是強類型的,setValue(Object)能夠接受任意類型,所以須要咱們本身判斷類型是否兼容;
(四、須要本身編程實現驗證,Spring3支持更棒的註解驗證支持;
(五、在使用SpEL表達式語言或DataBinder時,只能進行String<--->Object之間的類型轉換;
(6
、不支持細粒度的類型轉換/格式化,如UserModel的registerDate須要轉換/格式化相似「
2012-05-01
」的數據,而OrderModel的orderDate須要轉換/格式化相似「2012-05-0115:11:13」的數據,由於你們都爲java.util.Date類型,所以不太容易進行細粒度轉換/格式化。
在Spring Web MVC環境中,數據類型轉換、驗證及格式化一般是這樣使用的:
流程:
①、類型轉換:首先表單數據(所有是字符串)經過WebDataBinder進行綁定到命令對象,內部經過PropertyEditor實現;
②:數據驗證:在控制器中的功能處理方法中,須要顯示的調用Spring的Validator實現並將錯誤信息添加到BindingResult對象中;
③:格式化顯示:在表單頁面能夠經過以下方式展現經過PropertyEditor
格式化的數據和錯誤信息:
首先須要經過如上taglib指令引入spring的兩個標籤庫。
如上PropertyEditor和驗證API使用起來比較麻煩,並且有許多缺點,所以Spring3提供了更強大的類型轉換(Type Conversion)支持,它能夠在任意對象之間進行類型轉換,不只僅是String<——>Object;也提供了強大的數據驗證支持;同時提供了強大的數據格式化支持。
2、從Spring3開始,咱們可使用以下架構進行類型轉換、驗證及格式化:
流程:
①:類型轉換:內部的ConversionService會根據S源類型/T目標類型自動選擇相應的Converter SPI進行類型轉換,並且是強類型的,能在任意類型數據之間進行轉換;
②:數據驗證:支持JSR-303驗證框架,如將@Valid放在須要驗證的目標類型上便可;
③:格式化顯示:其實就是任意目標類型---->String的轉換,徹底可使用Converter SPI完成。
Spring爲了更好的詮釋格式化/解析功能提供了Formatter SPI,支持根據Locale信息進行格式化/解析,並且該套SPI能夠支持字段/參數級別的細粒度格式化/解析,流程以下:
①:類型解析(轉換):String---->T類型目標對象的解析,和PropertyEditor相似;
③:格式化顯示:任意目標類型---->String的轉換,和PropertyEditor相似。
Formatter SPI最大特色是能進行字段/參數級別的細粒度解析/格式化控制,即便是Converter SPI也是粗粒度的(到某個具體類型,而不是其中的某個字段單獨控制),目前Formatter SPI還不是很完善,若是您有好的想法能夠到Spring官網提建議。
Formatter SPI內部實現實際委託給Converter SPI進行轉換,即約束爲解析/格式化String<---->任意目標類型。
在Spring Web MVC環境中,數據類型轉換、驗證及格式化一般是這樣使用的:
①、類型轉換:首先表單數據(所有是字符串)經過WebDataBinder進行綁定到命令對象,內部經過Converter SPI實現;
②:數據驗證:使用JSR-303驗證框架進行驗證;
③:格式化顯示:在表單頁面能夠經過以下方式展現經過內部經過Converter SPI
格式化的數據和錯誤信息:
首先須要經過如上taglib指令引入spring的兩個標籤庫。
如上代碼能工做的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的做用是暴露conversionService到請求中以便如<spring:eval>標籤使用。
接下來咱們就詳細學習一下這些知識吧。