<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # Spring3 Web MVC下的數據類型轉換(第一篇)——《跟我學Spring3 Web MVC》搶先看 基于spring-framework-3.1.1.RELEASE ### 7.1、簡介 在編寫可視化界面項目時,我們通常需要對數據進行類型轉換、驗證及格式化。 **一、在Spring3之前,我們使用如下架構進行類型轉換、驗證及格式化:** ![](https://box.kancloud.cn/2016-05-13_57354724bd86c.jpg) **流程:** ①:類型轉換:首先調用PropertyEditor的setAsText(String),內部根據需要調用setValue(Object)方法進行設置轉換后的值; ②:數據驗證:需要顯示調用Spring的Validator接口實現進行數據驗證; ③:格式化顯示:需要調用PropertyEditor的getText進行格式化顯示。 **使用如上架構的缺點是:** (1、PropertyEditor被設計為只能String&lt;——&gt;Object之間轉換,不能任意對象類型&lt;——&gt;任意類型,如我們常見的Long時間戳到Date類型的轉換是辦不到的; (2、PropertyEditor是線程不安全的,也就是有狀態的,因此每次使用時都需要創建一個,不可重用; (3、PropertyEditor不是強類型的,setValue(Object)可以接受任意類型,因此需要我們自己判斷類型是否兼容; (4、需要自己編程實現驗證,Spring3支持更棒的注解驗證支持; (5、在使用SpEL表達式語言或DataBinder時,只能進行String&lt;---&gt;Object之間的類型轉換; `(6`、`不支持細粒度的類型轉換/格式化,如UserModel的registerDate需要轉換/格式化類似“``2012-05-01``”的數據,而OrderModel的orderDate需要轉換/格式化類似“2012-05-01 15:11:13”的數據,因為大家都為java.util.Date類型,因此不太容易進行細粒度轉換/格式化。` `**在Spring Web MVC環境中,數據類型轉換、驗證及格式化通常是這樣使用的:**` ![](https://box.kancloud.cn/2016-05-13_57354724d0bc1.jpg) **流程:** `①、類型轉換:首先表單數據(全部是字符串)通過WebDataBinder進行綁定到命令對象,內部通過PropertyEditor實現;` ②:數據驗證:在控制器中的功能處理方法中,需要顯示的調用Spring的Validator實現并將錯誤信息添加到BindingResult對象中; ③:格式化顯示:在表單頁面可以通過如下方式展示通過`PropertyEditor`格式化的數據和錯誤信息: ``` <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> ``` 首先需要通過如上taglib指令引入spring的兩個標簽庫。 ``` //1、格式化單個命令/表單對象的值(好像比較麻煩,真心沒有好辦法) <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind> ``` ``` //2、<spring:eval>標簽,自動調用ConversionService并選擇相應的Converter SPI進行格式化展示 <spring:eval expression="dataBinderTest.phoneNumber"></spring:eval> ``` 如上代碼能工作的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的作用是暴露conversionService到請求中以便如&lt;spring:eval&gt;標簽使用。 ``` //3、通過form標簽,內部的表單標簽會自動調用命令/表單對象屬性對應的PropertyEditor進行格式化顯示 <form:form commandName="dataBinderTest"> <form:input path="phoneNumber"/><!-- 如果出錯會顯示錯誤之前的數據而不是空 --> </form:form> ``` ``` //4、顯示驗證失敗后的錯誤信息 <form:errors></form:errors> ``` 接下來我們就詳細學習一下這些知識吧。 ## 7.2、數據類型轉換 ### 7.2.1、Spring3之前的PropertyEditor PropertyEditor介紹請參考【4.16.1、數據類型轉換】。 **一、測試之前我們需要準備好測試環境:** (1、模型對象,和【4.16.1、數據類型轉換】使用的一樣,需要將DataBinderTestModel模型類及相關類拷貝過來放入cn.javass.chapter7.model包中。 (2、控制器定義: ``` package cn.javass.chapter7.web.controller; //省略import @Controller public class DataBinderTestController { @RequestMapping(value = "/dataBind") public String test(DataBinderTestModel command) { //輸出command對象看看是否綁定正確 System.out.println(command); model.addAttribute("dataBinderTest", command); return "bind/success"; } } ``` (3、Spring配置文件定義,請參考chapter7-servlet.xml,并注冊DataBinderTestController: ``` <bean class="cn.javass.chapter7.web.controller.DataBinderTestController"/> ``` (4、測試的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 **二、注解式控制器注冊PropertyEditor:** **1、使用WebDataBinder進行控制器級別注冊PropertyEditor(控制器獨享)** ``` @InitBinder //此處的參數也可以是ServletRequestDataBinder類型 public void initBinder(WebDataBinder binder) throws Exception { //注冊自定義的屬性編輯器 //1、日期 DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); CustomDateEditor dateEditor = new CustomDateEditor(df, true); //表示如果命令對象有Date類型的屬性,將使用該屬性編輯器進行類型轉換 binder.registerCustomEditor(Date.class, dateEditor); //自定義的電話號碼編輯器(和【4.16.1、數據類型轉換】一樣) binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEditor()); } ``` 和【4.16.1、數據類型轉換】一節類似,只是此處需要通過@InitBinder來注冊自定義的PropertyEditor。 **2、使用**`**WebBindingInitializer批量注冊**`**PropertyEditor** 和【4.16.1、數據類型轉換】不太一樣,因為我們的注解式控制器是POJO,沒有實現任何東西,因此無法注入WebBindingInitializer,此時我們需要把WebBindingInitializer注入到我們的RequestMappingHandlerAdapter或AnnotationMethodHandlerAdapter,這樣對于所有的注解式控制器都是共享的。 ``` <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="webBindingInitializer"> <bean class="cn.javass.chapter7.web.controller.support.initializer.MyWebBindingInitializer"/> </property> </bean> ``` 此時我們注釋掉控制器級別通過@InitBinder注冊PropertyEditor的方法。 **3、全局級別注冊PropertyEditor(全局共享)** 和【4.16.1、數據類型轉換】一節一樣,此處不再重復。請參考【4.16.1、數據類型轉換】的【全局級別注冊PropertyEditor(全局共享)】。 `接下來我們看一下Spring3提供的更強大的類型轉換支持。` ### 7.2.2、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&lt;----&gt;java.lang.Long或java.lang.String----&gt;PhoneNumberModel等。 #### 7.2.2.1、架構 **1、類型轉換器:**提供類型轉換的實現支持。 ![](https://box.kancloud.cn/2016-05-13_57354724e860b.jpg) 一個有如下三種接口: **(1、Converter:**類型轉換器,用于轉換S類型到T類型,此接口的實現必須是線程安全的且可以被共享。 ``` package org.springframework.core.convert.converter; public interface Converter<S, T> { //① S是源類型 T是目標類型 T convert(S source); //② 轉換S類型的source到T目標類型的轉換方法 } ``` 示例:請參考cn.javass.chapter7.converter.support.StringToPhoneNumberConverter轉換器,用于將String---&gt;PhoneNumberModel。 此處我們可以看到Converter接口實現只能轉換一種類型到另一種類型,不能進行多類型轉換,如將一個數組轉換成集合,如(String[] ----&gt; List&lt;String&gt;、String[]-----&gt;List&lt;PhoneNumberModel&gt;等)。 **(2、GenericConverter和ConditionalGenericConverter:**GenericConverter接口實現能在多種類型之間進行轉換,ConditionalGenericConverter是有條件的在多種類型之間進行轉換。 ``` package org.springframework.core.convert.converter; public interface GenericConverter { Set<ConvertiblePair> getConvertibleTypes(); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); } ``` getConvertibleTypes:指定了可以轉換的目標類型對; convert:在sourceType和targetType類型之間進行轉換。 ``` package org.springframework.core.convert.converter; public interface ConditionalGenericConverter extends GenericConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); } ``` matches:用于判斷sourceType和targetType類型之間能否進行類型轉換。 示例:如org.springframework.core.convert.support.ArrayToCollectionConverter和CollectionToArrayConverter用于在數組和集合間進行轉換的ConditionalGenericConverter實現,如在String[]&lt;----&gt;List&lt;String&gt;、String[]&lt;----&gt;List&lt;PhoneNumberModel&gt;等之間進行類型轉換。 對于我們大部分用戶來說一般不需要自定義GenericConverter, 如果需要可以參考內置的GenericConverter來實現自己的。 **(3、ConverterFactory:**工廠模式的實現,用于選擇將一種S源類型轉換為R類型的子類型T的轉換器的工廠接口。 ``` package org.springframework.core.convert.converter; public interface ConverterFactory<S, R> { <T extends R> Converter<S, T> getConverter(Class<T> targetType); } ``` S:源類型;R目標類型的父類型;T:目標類型,且是R類型的子類型; getConverter:得到目標類型的對應的轉換器。 示例:如org.springframework.core.convert.support.NumberToNumberConverterFactory用于在Number類型子類型之間進行轉換,如Integer---&gt;Double, Byte----&gt;Integer, Float---&gt;Double等。 對于我們大部分用戶來說一般不需要自定義ConverterFactory,如果需要可以參考內置的ConverterFactory來實現自己的。 **2、類型轉換器注冊器、類型轉換服務:**提供類型轉換器注冊支持,運行時類型轉換API支持。 ![](https://box.kancloud.cn/2016-05-13_5735472509463.jpg) 一共有如下兩種接口: **(1、ConverterRegistry:**類型轉換器注冊支持,可以注冊/刪除相應的類型轉換器。 ``` package org.springframework.core.convert.converter; public interface ConverterRegistry { void addConverter(Converter<?, ?> converter); void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter); void addConverter(GenericConverter converter); void addConverterFactory(ConverterFactory<?, ?> converterFactory); void removeConvertible(Class<?> sourceType, Class<?> targetType); } ``` 可以注冊:Converter實現,GenericConverter實現,ConverterFactory實現。 **(2、ConversionService:**運行時類型轉換服務接口,提供運行期類型轉換的支持。 ``` package org.springframework.core.convert; public interface ConversionService { boolean canConvert(Class<?> sourceType, Class<?> targetType); boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); <T> T convert(Object source, Class<T> targetType); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); } ``` convert:將源對象轉換為目標類型的目標對象。 Spring提供了兩個默認實現(其都實現了ConverterRegistry**、**ConversionService接口): DefaultConversionService:默認的類型轉換服務實現; DefaultFormattingConversionService:帶數據格式化支持的類型轉換服務實現,一般使用該服務實現即可。 #### 7.2.2.2、Spring內建的類型轉換器如下所示:? ? | **類名** | **說明** | | --- | --- | --- | | **第一組:標量轉換器** | | StringToBooleanConverter | String-----&gt;Booleantrue:true/on/yes/1; false:false/off/no/0 | | ObjectToStringConverter | Object-----&gt;String調用toString方法轉換 | | StringToNumberConverterFactory | String-----&gt;Number(如Integer、Long等) | | NumberToNumberConverterFactory | Number子類型(Integer、Long、Double等)&lt;——&gt; Number子類型(Integer、Long、Double等) | | StringToCharacterConverter | String-----&gt;java.lang.Character取字符串第一個字符 | | NumberToCharacterConverter | Number子類型(Integer、Long、Double等)——&gt; java.lang.Character | | CharacterToNumberFactory | java.lang.Character ——&gt;Number子類型(Integer、Long、Double等) | | StringToEnumConverterFactory | String-----&gt;enum類型通過Enum.valueOf將字符串轉換為需要的enum類型 | | EnumToStringConverter | enum類型-----&gt;String返回enum對象的name()值 | | StringToLocaleConverter | String-----&gt;java.util.Local | | PropertiesToStringConverter | java.util.Properties-----&gt;String默認通過ISO-8859-1解碼 | | StringToPropertiesConverter | String-----&gt;java.util.Properties默認使用ISO-8859-1編碼 | | **第二組:集合、數組相關轉換器** | | ArrayToCollectionConverter | 任意S數組----&gt;任意T集合(List、Set) | | CollectionToArrayConverter | 任意T集合(List、Set)----&gt;任意S數組 | | ArrayToArrayConverter | 任意S數組&lt;----&gt;任意T數組 | | CollectionToCollectionConverter | 任意T集合(List、Set)&lt;----&gt;任意T集合(List、Set)即集合之間的類型轉換 | | MapToMapConverter | Map&lt;----&gt;Map之間的轉換 | | ArrayToStringConverter | 任意S數組----&gt;String類型 | | StringToArrayConverter | String-----&gt;數組默認通過“,”分割,且去除字符串的兩邊空格(trim) | | ArrayToObjectConverter | 任意S數組----&gt;任意Object的轉換(如果目標類型和源類型兼容,直接返回源對象;否則返回S數組的第一個元素并進行類型轉換) | | ObjectToArrayConverter | Object-----&gt;單元素數組 | | CollectionToStringConverter | 任意T集合(List、Set)----&gt;String類型 | | StringToCollectionConverter | String-----&gt;集合(List、Set)默認通過“,”分割,且去除字符串的兩邊空格(trim) | | CollectionToObjectConverter | 任意T集合----&gt;任意Object的轉換(如果目標類型和源類型兼容,直接返回源對象;否則返回S數組的第一個元素并進行類型轉換) | | ObjectToCollectionConverter | Object-----&gt;單元素集合 | | **第三組:默認(fallback)轉換器:**之前的轉換器不能轉換時調用 | | ObjectToObjectConverter | Object(S)-----&gt;Object(T)首先嘗試valueOf進行轉換、沒有則嘗試new 構造器(S) | | IdToEntityConverter | Id(S)-----&gt;Entity(T)查找并調用public static T find[EntityName](S)獲取目標對象,EntityName是T類型的簡單類型 | | FallbackObjectToStringConverter | Object-----&gt;StringConversionService作為恢復使用,即其他轉換器不能轉換時調用(執行對象的toString()方法) | S:代表源類型,T:代表目標類型 如上的轉換器在使用轉換服務實現DefaultConversionService和DefaultFormattingConversionService時會自動注冊。 #### 7.2.2.3、示例 (1、自定義String-----&gt;PhoneNumberModel的轉換器 ``` package cn.javass.chapter7.web.controller.support.converter; //省略import public class StringToPhoneNumberConverter implements Converter<String, PhoneNumberModel> { Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$"); @Override public PhoneNumberModel convert(String source) { if(!StringUtils.hasLength(source)) { //①如果source為空 返回null return null; } Matcher matcher = pattern.matcher(source); if(matcher.matches()) { //②如果匹配 進行轉換 PhoneNumberModel phoneNumber = new PhoneNumberModel(); phoneNumber.setAreaCode(matcher.group(1)); phoneNumber.setPhoneNumber(matcher.group(2)); return phoneNumber; } else { //③如果不匹配 轉換失敗 throw new IllegalArgumentException(String.format("類型轉換失敗,需要格式[010-12345678],但格式是[%s]", source)); } } } ``` String轉換為Date的類型轉換器,請參考cn.javass.chapter7.web.controller.support.converter.StringToDateConverter。 (2、測試用例(cn.javass.chapter7.web.controller.support.converter.ConverterTest) ``` @Test public void testStringToPhoneNumberConvert() { DefaultConversionService conversionService = new DefaultConversionService(); conversionService.addConverter(new StringToPhoneNumberConverter()); String phoneNumberStr = "010-12345678"; PhoneNumberModel phoneNumber = conversionService.convert(phoneNumberStr, PhoneNumberModel.class); Assert.assertEquals("010", phoneNumber.getAreaCode()); } ``` 類似于PhoneNumberEditor將字符串“010-12345678”轉換為PhoneNumberModel。 ``` @Test public void testOtherConvert() { DefaultConversionService conversionService = new DefaultConversionService(); //"1"--->true(字符串“1”可以轉換為布爾值true) Assert.assertEquals(Boolean.valueOf(true), conversionService.convert("1", Boolean.class)); //"1,2,3,4"--->List(轉換完畢的集合大小為4) Assert.assertEquals(4, conversionService.convert("1,2,3,4", List.class).size()); } ``` 其他類型轉換器使用也是類似的,此處不再重復。 #### 7.2.2.4、集成到Spring Web MVC環境 (1、注冊ConversionService實現和自定義的類型轉換器 ``` <!-- ①注冊ConversionService --> <bean id="conversionService" class="org.springframework.format.support. FormattingConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="cn.javass.chapter7.web.controller.support. converter.StringToPhoneNumberConverter"/> <bean class="cn.javass.chapter7.web.controller.support. converter.StringToDateConverter"> <constructor-arg value="yyyy-MM-dd"/> </bean> </list> </property> </bean> ``` FormattingConversionServiceFactoryBean:是FactoryBean實現,默認使用DefaultFormattingConversionService轉換器服務實現; converters:注冊我們自定義的類型轉換器,此處注冊了String---&gt;PhoneNumberModel和String---&gt;Date的類型轉換器。 (2、通過ConfigurableWebBindingInitializer注冊ConversionService ``` <!-- ②使用ConfigurableWebBindingInitializer注冊conversionService --> <bean id="webBindingInitializer" class="org.springframework.web.bind.support. ConfigurableWebBindingInitializer"> <property name="conversionService" ref="conversionService"/> </bean> ``` 此處我們通過ConfigurableWebBindingInitializer綁定初始化器進行ConversionService的注冊; 3、注冊ConfigurableWebBindingInitializer到RequestMappingHandlerAdapter ``` <bean class="org.springframework.web.servlet.mvc.method.annotation. RequestMappingHandlerAdapter"> <property name="webBindingInitializer" ref="webBindingInitializer"/> </bean> ``` 通過如上配置,我們就完成了Spring3.0的類型轉換系統與Spring Web MVC的集成。此時可以啟動服務器輸入之前的URL測試了。 此時可能有人會問,如果我同時使用PropertyEditor和ConversionService,執行順序是什么呢?內部首先查找PropertyEditor進行類型轉換,如果沒有找到相應的PropertyEditor再通過ConversionService進行轉換。 如上集成過程看起來比較麻煩,后邊我們會介紹&lt;mvc:annotation-driven&gt;和@EnableWebMvc,ConversionService會自動注冊,后續章節再詳細介紹。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看