类型转换

②:数据验证:须要显示调用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格式化的数据和错误信息:

 

Java代码  
  1. <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>  
  2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>   

 

首先须要经过如上taglib指令引入spring的两个标签库。

Java代码  
  1. //一、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)  
  2. <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>   
Java代码  
  1. //二、经过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示  
  2. <form:form commandName="dataBinderTest">  
  3.     <form:input path="phoneNumber"/><!-- 若是出错会显示错误以前的数据而不是空 -->  
  4. </form:form>   
Java代码  
  1. //三、显示验证失败后的错误信息  
  2. <form:errors></form:errors>   

如上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格式化的数据和错误信息:

Java代码  
  1. <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>  
  2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>   

首先须要经过如上taglib指令引入spring的两个标签库。

Java代码  
  1. //一、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)  
  2. <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>   
Java代码  
  1. //二、<spring:eval>标签,自动调用ConversionService并选择相应的Converter SPI进行格式化展现  
  2. <spring:eval expression="dataBinderTest.phoneNumber"></spring:eval>   

如上代码能工做的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的做用是暴露conversionService到请求中以便如<spring:eval>标签使用。

Java代码  
  1. //三、经过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示  
  2. <form:form commandName="dataBinderTest">  
  3.     <form:input path="phoneNumber"/><!-- 若是出错会显示错误以前的数据而不是空 -->  
  4. </form:form>   
Java代码  
  1. //四、显示验证失败后的错误信息  
  2. <form:errors></form:errors>   

接下来咱们就详细学习一下这些知识吧。

 

 

7.二、数据类型转换

7.2.一、Spring3以前的PropertyEditor

PropertyEditor介绍请参考【4.16.一、数据类型转换】。

 

1、测试以前咱们须要准备好测试环境:

(一、模型对象,和【4.16.一、数据类型转换】使用的同样,须要将DataBinderTestModel模型类及相关类拷贝过来放入cn.javass.chapter7.model包中。

(二、控制器定义:         

 

Java代码  
  1. package cn.javass.chapter7.web.controller;  
  2. //省略import  
  3. @Controller  
  4. public class DataBinderTestController {  
  5.     @RequestMapping(value = "/dataBind")  
  6.     public String test(DataBinderTestModel command) {  
  7.             //输出command对象看看是否绑定正确  
  8.         System.out.println(command);  
  9.         model.addAttribute("dataBinderTest", command);  
  10.         return "bind/success";  
  11.     }  
  12. }  

 

 (三、Spring配置文件定义,请参考chapter7-servlet.xml,并注册DataBinderTestController:

 

Java代码  
  1. <bean class="cn.javass.chapter7.web.controller.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(控制器独享)

Java代码  
  1. @InitBinder  
  2. //此处的参数也能够是ServletRequestDataBinder类型  
  3. public void initBinder(WebDataBinder binder) throws Exception {  
  4.     //注册自定义的属性编辑器  
  5.     //一、日期  
  6.     DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  7.     CustomDateEditor dateEditor = new CustomDateEditor(df, true);  
  8.     //表示若是命令对象有Date类型的属性,将使用该属性编辑器进行类型转换  
  9.     binder.registerCustomEditor(Date.class, dateEditor);      
  10.     //自定义的电话号码编辑器(和【4.16.一、数据类型转换】同样)  
  11.     binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEditor());  
  12. }   

和【4.16.一、数据类型转换】一节相似,只是此处须要经过@InitBinder来注册自定义的PropertyEditor。

 

二、使用WebBindingInitializer批量注册PropertyEditor

和【4.16.一、数据类型转换】不太同样,由于咱们的注解式控制器是POJO,没有实现任何东西,所以没法注入WebBindingInitializer,此时咱们须要把WebBindingInitializer注入到咱们的RequestMappingHandlerAdapter或AnnotationMethodHandlerAdapter,这样对于全部的注解式控制器都是共享的。

Java代码  
  1. <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">  
  2.   <property name="webBindingInitializer">  
  3.     <bean class="cn.javass.chapter7.web.controller.support.initializer.MyWebBindingInitializer"/>  
  4.   </property>  
  5. </bean>   

此时咱们注释掉控制器级别经过@InitBinder注册PropertyEditor的方法。

三、全局级别注册PropertyEditor(全局共享)

和【4.16.一、数据类型转换】一节同样,此处再也不重复。请参考【4.16.一、数据类型转换】的【全局级别注册PropertyEditor(全局共享)】。

 

接下来咱们看一下Spring3提供的更强大的类型转换支持。

 

7.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<---->java.lang.Long或java.lang.String---->PhoneNumberModel等。

 

7.2.2.一、架构

一、类型转换器:提供类型转换的实现支持。

 

 

一个有以下三种接口:

(一、Converter:类型转换器,用于转换S类型到T类型,此接口的实现必须是线程安全的且能够被共享。

Java代码  
  1. package org.springframework.core.convert.converter;  
  2. public interface Converter<S, T> { //① S是源类型 T是目标类型  
  3.     T convert(S source); //② 转换S类型的source到T目标类型的转换方法  
  4. }   

示例:请参考cn.javass.chapter7.converter.support.StringToPhoneNumberConverter转换器,用于将String--->PhoneNumberModel。

 

此处咱们能够看到Converter接口实现只能转换一种类型到另外一种类型,不能进行多类型转换,如将一个数组转换成集合,如(String[] ----> List<String>、String[]----->List<PhoneNumberModel>等)。

 

(二、GenericConverter和ConditionalGenericConverter:GenericConverter接口实现能在多种类型之间进行转换,ConditionalGenericConverter是有条件的在多种类型之间进行转换。

Java代码  
  1. package org.springframework.core.convert.converter;  
  2. public interface GenericConverter {  
  3.     Set<ConvertiblePair> getConvertibleTypes();  
  4.     Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);  
  5. }  

getConvertibleTypes:指定了能够转换的目标类型对;

convert:在sourceType和targetType类型之间进行转换。

Java代码  
  1. package org.springframework.core.convert.converter;  
  2. public interface ConditionalGenericConverter extends GenericConverter {  
  3.     boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);  
  4. }   

matches:用于判断sourceType和targetType类型之间可否进行类型转换。

 

示例:如org.springframework.core.convert.support.ArrayToCollectionConverter和CollectionToArrayConverter用于在数组和集合间进行转换的ConditionalGenericConverter实现,如在String[]<---->List<String>、String[]<---->List<PhoneNumberModel>等之间进行类型转换。

 

对于咱们大部分用户来讲通常不须要自定义GenericConverter, 若是须要能够参考内置的GenericConverter来实现本身的。

 

(三、ConverterFactory:工厂模式的实现,用于选择将一种S源类型转换为R类型的子类型T的转换器的工厂接口。

Java代码  
  1. package org.springframework.core.convert.converter;  
  2. public interface ConverterFactory<S, R> {  
  3.     <T extends R> Converter<S, T> getConverter(Class<T> targetType);  
  4. }   

S:源类型;R目标类型的父类型;T:目标类型,且是R类型的子类型;

getConverter:获得目标类型的对应的转换器。

 

示例:如org.springframework.core.convert.support.NumberToNumberConverterFactory用于在Number类型子类型之间进行转换,如Integer--->Double, Byte---->Integer, Float--->Double等。

 

对于咱们大部分用户来讲通常不须要自定义ConverterFactory,若是须要能够参考内置的ConverterFactory来实现本身的。

 

二、类型转换器注册器、类型转换服务:提供类型转换器注册支持,运行时类型转换API支持。

 

一共有以下两种接口:

(一、ConverterRegistry:类型转换器注册支持,能够注册/删除相应的类型转换器。

Java代码  
  1. package org.springframework.core.convert.converter;  
  2. public interface ConverterRegistry {  
  3.     void addConverter(Converter<?, ?> converter);  
  4.     void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter);  
  5.     void addConverter(GenericConverter converter);  
  6.     void addConverterFactory(ConverterFactory<?, ?> converterFactory);  
  7.     void removeConvertible(Class<?> sourceType, Class<?> targetType);  
  8. }   

能够注册:Converter实现,GenericConverter实现,ConverterFactory实现。

 

(二、ConversionService:运行时类型转换服务接口,提供运行期类型转换的支持。

Java代码  
  1. package org.springframework.core.convert;  
  2. public interface ConversionService {  
  3.     boolean canConvert(Class<?> sourceType, Class<?> targetType);  
  4.     boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);  
  5.     <T> T convert(Object source, Class<T> targetType);  
  6.     Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);  
  7. }   

convert:将源对象转换为目标类型的目标对象。

 

Spring提供了两个默认实现(其都实现了ConverterRegistryConversionService接口):

DefaultConversionService:默认的类型转换服务实现;

DefaultFormattingConversionService:带数据格式化支持的类型转换服务实现,通常使用该服务实现便可。

 

7.2.2.二、Spring内建的类型转换器以下所示:

类名

说明

第一组:标量转换器

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时会自动注册。

 

7.2.2.三、示例

(一、自定义String----->PhoneNumberModel的转换器

 

Java代码  
  1. package cn.javass.chapter7.web.controller.support.converter;  
  2. //省略import  
  3. public class StringToPhoneNumberConverter implements Converter<String, PhoneNumberModel> {  
  4.     Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");  
  5.     @Override  
  6.     public PhoneNumberModel convert(String source) {          
  7.         if(!StringUtils.hasLength(source)) {  
  8.             //①若是source为空 返回null  
  9.             return null;  
  10.         }  
  11.         Matcher matcher = pattern.matcher(source);  
  12.         if(matcher.matches()) {  
  13.             //②若是匹配 进行转换  
  14.             PhoneNumberModel phoneNumber = new PhoneNumberModel();  
  15.             phoneNumber.setAreaCode(matcher.group(1));  
  16.             phoneNumber.setPhoneNumber(matcher.group(2));  
  17.             return phoneNumber;  
  18.         } else {  
  19.             //③若是不匹配 转换失败  
  20.             throw new IllegalArgumentException(String.format("类型转换失败,须要格式[010-12345678],但格式是[%s]", source));  
  21.         }  
  22.     }  
  23. }  

String转换为Date的类型转换器,请参考cn.javass.chapter7.web.controller.support.converter.StringToDateConverter。

 

(二、测试用例(cn.javass.chapter7.web.controller.support.converter.ConverterTest)

Java代码  
  1. @Test  
  2. public void testStringToPhoneNumberConvert() {  
  3.     DefaultConversionService conversionService = new DefaultConversionService();  
  4.     conversionService.addConverter(new StringToPhoneNumberConverter());  
  5.       
  6.     String phoneNumberStr = "010-12345678";  
  7.     PhoneNumberModel phoneNumber = conversionService.convert(phoneNumberStr, PhoneNumberModel.class);  
  8.           
  9.     Assert.assertEquals("010", phoneNumber.getAreaCode());  
  10. }   

相似于PhoneNumberEditor将字符串“010-12345678”转换为PhoneNumberModel。  

 

 

Java代码  
  1. @Test  
  2. public void testOtherConvert() {  
  3.     DefaultConversionService conversionService = new DefaultConversionService();  
  4.       
  5.     //"1"--->true(字符串“1”能够转换为布尔值true)  
  6.     Assert.assertEquals(Boolean.valueOf(true), conversionService.convert("1", Boolean.class));  
  7.       
  8.     //"1,2,3,4"--->List(转换完毕的集合大小为4)  
  9.     Assert.assertEquals(4, conversionService.convert("1,2,3,4", List.class).size());  
  10. }   

其余类型转换器使用也是相似的,此处再也不重复。

 

7.2.2.四、集成到Spring Web MVC环境

(一、注册ConversionService实现和自定义的类型转换器

Java代码  
  1. <!-- ①注册ConversionService -->  
  2. <bean id="conversionService" class="org.springframework.format.support.  
  3.                                              FormattingConversionServiceFactoryBean">  
  4.     <property name="converters">  
  5.        <list>  
  6.             <bean class="cn.javass.chapter7.web.controller.support.  
  7.                              converter.StringToPhoneNumberConverter"/>  
  8.             <bean class="cn.javass.chapter7.web.controller.support.  
  9.                              converter.StringToDateConverter">  
  10.                 <constructor-arg value="yyyy-MM-dd"/>  
  11.             </bean>  
  12.         </list>  
  13.     </property>  
  14. </bean>   

FormattingConversionServiceFactoryBean:是FactoryBean实现,默认使用DefaultFormattingConversionService转换器服务实现;

converters:注册咱们自定义的类型转换器,此处注册了String--->PhoneNumberModel和String--->Date的类型转换器。

 

(二、经过ConfigurableWebBindingInitializer注册ConversionService

Java代码  
  1. <!-- ②使用ConfigurableWebBindingInitializer注册conversionService -->  
  2. <bean id="webBindingInitializer" class="org.springframework.web.bind.support.  
  3.                                                                         ConfigurableWebBindingInitializer">  
  4.     <property name="conversionService" ref="conversionService"/>  
  5. </bean>   

此处咱们经过ConfigurableWebBindingInitializer绑定初始化器进行ConversionService的注册;

 

三、注册ConfigurableWebBindingInitializer到RequestMappingHandlerAdapter

Java代码  
  1. <bean class="org.springframework.web.servlet.mvc.method.annotation.  
  2.                                                             RequestMappingHandlerAdapter">  
  3. <property name="webBindingInitializer" ref="webBindingInitializer"/>  
  4. </bean>   

经过如上配置,咱们就完成了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格式化的数据和错误信息:

 

  1. <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>  
  2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>   

 

首先须要经过如上taglib指令引入spring的两个标签库。

  1. //一、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)  
  2. <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>   
  1. //二、经过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示  
  2. <form:form commandName="dataBinderTest">  
  3.     <form:input path="phoneNumber"/><!-- 若是出错会显示错误以前的数据而不是空 -->  
  4. </form:form>   
  1. //三、显示验证失败后的错误信息  
  2. <form:errors></form:errors>   

如上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格式化的数据和错误信息:

  1. <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>  
  2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>   

首先须要经过如上taglib指令引入spring的两个标签库。

  1. //一、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)  
  2. <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>   
  1. //二、<spring:eval>标签,自动调用ConversionService并选择相应的Converter SPI进行格式化展现  
  2. <spring:eval expression="dataBinderTest.phoneNumber"></spring:eval>   

如上代码能工做的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的做用是暴露conversionService到请求中以便如<spring:eval>标签使用。

  1. //三、经过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示  
  2. <form:form commandName="dataBinderTest">  
  3.     <form:input path="phoneNumber"/><!-- 若是出错会显示错误以前的数据而不是空 -->  
  4. </form:form>   
  1. //四、显示验证失败后的错误信息  
  2. <form:errors></form:errors>   

接下来咱们就详细学习一下这些知识吧。

相关文章
相关标签/搜索