现在的位置: 首页 > 综合 > 正文

Spring3 Web MVC下的数据类型转换

2012年11月14日 ⁄ 综合 ⁄ 共 6310字 ⁄ 字号 评论关闭

基于spring-framework-3.1.1.RELEASE

7.1、简介

在编写可视化界面项目时,我们通常需要对数据进行类型转换、验证及格式化。

 

一、在Spring3之前,我们使用如下架构进行类型转换、验证及格式化:


 

流程:

①:类型转换:首先调用PropertyEditor的setAsText(String),内部根据需要调用setValue(Object)方法进行设置转换后的值;

②:数据验证:需要显示调用Spring的Validator接口实现进行数据验证;

③:格式化显示:需要调用PropertyEditor的getText进行格式化显示。

 

使用如上架构的缺点是:

(1、PropertyEditor被设计为只能String<——>Object之间转换,不能任意对象类型<——>任意类型,如我们常见的Long时间戳到Date类型的转换是办不到的;

(2、PropertyEditor是线程不安全的,也就是有状态的,因此每次使用时都需要创建一个,不可重用;

(3、PropertyEditor不是强类型的,setValue(Object)可以接受任意类型,因此需要我们自己判断类型是否兼容;

(4、需要自己编程实现验证,Spring3支持更棒的注解验证支持;

(5、在使用SpEL表达式语言或DataBinder时,只能进行String<--->Object之间的类型转换;

(6不支持细粒度的类型转换/格式化,如UserModel的registerDate需要转换/格式化类似“2012-05-01”的数据,而OrderModel的orderDate需要转换/格式化类似“2012-05-01 15:11:13”的数据,因为大家都为java.util.Date类型,因此不太容易进行细粒度转换/格式化。

 

在Spring Web 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. //1、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)  
  2. <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>  

 

Java代码  收藏代码
  1. //2、<spring:eval>标签,自动调用ConversionService并选择相应的Converter SPI进行格式化展示  
  2. <spring:eval expression="dataBinderTest.phoneNumber"></spring:eval>  

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

 

Java代码  收藏代码
  1. //3、通过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示  
  2. <form:form commandName="dataBinderTest">  
  3.     <form:input path="phoneNumber"/><!-- 如果出错会显示错误之前的数据而不是空 -->  
  4. </form:form>  

 

Java代码  收藏代码
  1. //4、显示验证失败后的错误信息  
  2. <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、控制器定义:

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. }  

 

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

Java代码  收藏代码
  1. <bean class="cn.javass.chapter7.web.controller.DataBinderTestController"/>  
  2.       

 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(控制器独享)

Java代码  收藏代码
  1. @InitBinder  
  2. //此处的参数也可以是ServletRequestDataBinder类型  
  3. public void initBinder(WebDataBinder binder) throws Exception {  
  4.     //注册自定义的属性编辑器  
  5.     //1、日期  
  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.1、数据类型转换】一样)  
  11.     binder.registerCustomEditor(PhoneNumberModel.classnew PhoneNumberEditor());  
  12. }  

 和【4.16.1、数据类型转换】一节类似,只是此处需要通过@InitBinder来注册自定义的PropertyEditor。

 

2、使用WebBindingInitializer批量注册PropertyEditor

4.16.1、数据类型转换】不太一样,因为我们的注解式控制器是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的方法。

 

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

7.2.2.1、架构

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

 

一个有如下三种接口:

(1、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>等)。

 

(2、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来实现自己的。

 

(3、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&l

抱歉!评论已关闭.