他姐姐怎么突然之间微信屏蔽某人我了

Spring MVC学习(七)-------SpringMVC数据类型转换_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Spring MVC学习(七)-------SpringMVC数据类型转换
上传于||文档简介
&&S​p​r​i​n​g​ ​M​V​C​学​习​(​七​)​-​-​-​-​-​-​-​S​p​r​i​n​g​M​V​C​数​据​类​型​转​换
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩14页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢在实际的软件开发项目中,我们的&业务逻辑&常常需要我们对同样的数据进行各种变换。
例如,一个Web应用通过前端收集用户的输入成为Dto,然后将Dto转换成领域模型并持久化到数据库中。相反,当用户请求数据时,我们又需要做相反的工作:将从数据库中查询出来的领域模型以相反的方式转换成Dto再呈现给用户。
有时候我们还会面临更多的数据使用需求,例如有多个数据使用的客户端,每个客户端都有自己对数据结构的不同需求,而这也需要我们进行更多的数据转换。&频繁的数据转换琐碎而又凌乱,很多时候我们不得不做:&(1)在两个类型几乎只是名字不同而结构大体相似,却只能以手工的、逐个属性赋值的方式实现数据在类型间的&传递&。&(2)每遇到一个新的数据转换场景就手动实现一套转换逻辑,导致数据转换操作重复而又分散到应用的各个角落。&如果有这样一个&变形金刚&般的工具,把&橘子&变成我们想要的&苹果&,而我们需要做的只是定义好转换规则&&做我们真正的业务逻辑,或者甚至在简单场景下连规则都不需要定义(Convention Over Configuration),那将会是非常美好的事情。事实上在.NET中我们不用重复发明轮子,因为我们有&&AutoMapper,一个强大的Object-Object Mapping工具。&好吧,我承认自己有一点小小的激动,事实上我所做的项目正在经历以上的&困惑&,而AutoMapper确实带给我眼前一亮的感觉。因此我花了一点周末休息时间小小尝试了一把AutoMapper,通过做小的应用场景实现Dto到领域模型的映射,确实感觉到了它的&强大气场&。我将在文章中分享自己的使用心得,希望能给同样处于困惑中的你带来一点帮助。完整的项目代码我会在晚一些时候发布到自己的git repository中,欢迎大家自由参考使用。
【一】 将Model转换为Dto
先来看看我所&虚拟&的领域模型。这一次我定义了一个书店(BookStore):
public class BookStore
public string Name { }
public List&Book& Books { }
public Address Address { }
书店有自己的地址(Address):
public class Address
public string Country { }
public string City { }
public string Street { }
public string PostCode { }
同时书店里放了n本书(Book):
public class Book
public string Title { }
public string Description { }
public string Language { }
public decimal Price { }
public List&Author& Authors { }
public DateTime? PublishDate { }
public Publisher Publisher { }
public int? Paperback { }
每本书都有出版商信息(Publisher):
public class Publisher
public string Name { }
每本书可以有最多2个作者的信息(Author):
public class Author
public string Name { }
public string Description { }
public ContactInfo ContactInfo { }
每个作者都有自己的联系方式(ContactInfo):
public class ContactInfo
public string Email { }
public string Blog { }
public string Twitter { }
差不多就是这样了,一个有着层级结构的领域模型。&再来看看我们的Dto结构。&在Dto中我们有与BookStore对应的BookStoreDto:
public class BookStoreDto
public string Name { }
public List&BookDto& Books { }
public AddressDto Address { }
其中包含与Address对应的AddressDto:
public class AddressDto
public string Country { }
public string City { }
public string Street { }
public string PostCode { }
以及与Book相对应的BookDto:
public class BookDto
public string Title { }
public string Description { }
public string Language { }
public decimal Price { }
public DateTime? PublishDate { }
public string Publisher { }
public int? Paperback { }
public string FirstAuthorName { }
public string FirstAuthorDescription { }
public string FirstAuthorEmail { }
public string FirstAuthorBlog { }
public string FirstAuthorTwitter { }
public string SecondAuthorName { }
public string SecondAuthorDescription { }
public string SecondAuthorEmail { }
public string SecondAuthorBlog { }
public string SecondAuthorTwitter { }
注意到我们的BookDto&拉平了&整个Book的层级结构,一个BookDto里携带了Book及其所有Author、Publisher等所有模式的数据。&正好我们来看一下Dto到Model的映射规则。&(1)BookStoreDto && BookStore
BookStoreDto中的字段
BookStore中的字段
(2)AddressDto && Address
AddressDto中的字段
Address中的字段
(3)BookDto -& Book。&BookDto中的一些基本字段可以直接对应到Book中的字段。
BookDto中的字段
Book中的字段
Description
Description
PublishDate
PublishDate
每本书至多有2个作者,在BookDto中分别使用&First&前缀和&Second&前缀的字段来表示。因此,所有FirstXXX字段都将映射成Book的Authors中的第1个Author对象,而所有SecondXXX字段则将映射成Authors中的第2个Author对象。
BookDto中的字段
Book中的Authors中的第1个Author对象中的字段
FirstAuthorName
FirstAuthorDescription
Description
FirstAuthorEmail
ContactInfo.Email
FirstAuthorBlog
ContactInfo.Blog
FirstAuthorTwitter
ContactInfo.Twitter
注意上表中的ContactInfo.Email表示对应到Author对象的ContactInfo的Email字段,依次类推。类似的我们有:
BookDto中的字段
Book中的Authors中的第2个Author对象中的字段
SecondAuthorName
SecondAuthorDescription
Description
SecondAuthorEmail
ContactInfo.Email
SecondAuthorBlog
ContactInfo.Blog
SecondAuthorTwitter
ContactInfo.Twitter
最后还有Publisher字段,它将对应到一个独立的Publisher对象。
BookDto中的字段
Publisher中的字段
差不多就是这样了,我们的需求是要实现这一大坨Dto到另一大坨的Model之间的数据转换。
【二】将Dto转换为Model
1,以Convention方式实现零配置的对象映射
我们的AddressDto和Address结构完全一致,且字段名也完全相同。对于这样的类型转换,AutoMapper为我们提供了Convention,正如它的官网上所说的:
AutoMapper uses a convention-based matching algorithm to match up source to destination values.
我们要做的只是将要映射的两个类型告诉AutoMapper(调用Mapper类的Static方法CreateMap并传入要映射的类型):
&&& Mapper.CreateMap&AddressDto, Address&();&
然后就可以交给AutoMapper帮我们搞定一切了:
AddressDto dto = new AddressDto
Country = "China",
City = "Beijing",
Street = "Dongzhimen Street",
PostCode = "100001"
Address address = Mapper.Map&AddressDto,Address&(Dto);
address.Country.ShouldEqual("China");
address.City.ShouldEqual("Beijing");
address.Street.ShouldEqual("Dongzhimen Street");
address.PostCode.ShouldEqual("100001");
如果AddressDto中有值为空的属性,AutoMapper在映射的时候会把Address中的相应属性也置为空:
Address address = Mapper.Map&AddressDto,Address&(new AddressDto
Country = "China"
address.City.ShouldBeNull();
address.Street.ShouldBeNull();
address.PostCode.ShouldBeNull();
甚至如果传入一个空的AddressDto,AutoMapper也会帮我们得到一个空的Address对象。
Address address = Mapper.Map&AddressDto,Address&(null);
address.ShouldBeNull();
千万不要把这种Convention的映射方式当成&玩具&,它在映射具有相同字段名的复杂类型的时候还是具有相当大的威力的。&例如,考虑我们的BookStoreDto到BookStore的映射,两者的字段名称完全相同,只是字段的类型不一致。如果我们定义好了BookDto到Book的映射规则,再加上上述Convention方式的AddressDto到Address的映射,就可以用&零配置&实现BookStoreDto到BookStore的映射了:
IMappingExpression&BookDto, Book& expression = Mapper.CreateMap&BookDto,Book&();
// Define mapping rules from BookDto to Book here
Mapper.CreateMap&AddressDto, Address&();
Mapper.CreateMap&BookStoreDto, BookStore&();
然后我们就可以直接转换BookStoreDto了:
BookStoreDto dto = new BookStoreDto
Name = "My Store",
Address = new AddressDto
City = "Beijing"
Books = new List&BookDto&
new BookDto {Title = "RESTful Web Service"},
new BookDto {Title = "Ruby for Rails"},
BookStore bookStore = Mapper.Map&BookStoreDto,BookStore&(dto);
bookStore.Name.ShouldEqual("My Store");
bookStore.Address.City.ShouldEqual("Beijing");
bookStore.Books.Count.ShouldEqual(2);
bookStore.Books.First().Title.ShouldEqual("RESTful Web Service");
bookStore.Books.Last().Title.ShouldEqual("Ruby for Rails");
以下为自己补充:
(Begin)---------------------------------------------------------------
1, 要实现BookDto到Book之间的转换还是有一断路需要走的因为他嵌套了相应的子类型如:Publisher -&ContactInfo,
Author。废话少说,直接上答案:
var exp = Mapper.CreateMap&BookDto, Book&();
exp.ForMember(bok=& bok.Publisher/*(变量)*/,
(map) =& map.MapFrom(dto=&new Publisher(){Name= dto.Publisher/*(DTO的变量)*/}));
一般在我们写完规则之后通常会调用
//该方法主要用来检查还有那些规则没有写完。
Mapper.AssertConfigurationIsValid();
其它的就以此类推。
2,如果要完成 BookStore 到 BookStoreDto 具体的应该如何映射呢&相同的类型与名字就不说了,如BookStore.Name-&BookStoreDto.Name AutoMapper会自动去找。
而对于List&Book&与List&BookDto&者我们必须在配置下面代码之前
var exp = Mapper.CreateMap&BookStore, BookStoreDto&();
exp.ForMember(dto =& dto.Books, (map) =& map.MapFrom(m =& m.Books));
告诉AutoMapper,Book与BookDto的映射,最后效果为:
Mapper.CreateMap&Book, BookDto&();
var exp = Mapper.CreateMap&BookStore, BookStoreDto&();
exp.ForMember(dto =& dto.Books, (map) =& map.MapFrom(m =& m.Books));
Address同理。
3,如果要完成不同类型之间的转换用AutoMapper,如string到int,string-&DateTime,以及A-&B之间的类型转换我们可以参照如下例子:
4, 对于我们不想要某属性有值我们可以采用下面的方式。
exp.ForMember(ads =& ads.ZipCode, dto =& dto.Ignore()); //如果对于不想某属性有值,我们可以通过Ignore来忽略他,这样在调用AssertConfigurationIsValid时也不会报错.
(End)------------------------------------------------------------------------------------
【三】定义类型间的简单映射规则
前面我们看了Convention的映射方式,客观的说还是有很多类型间的映射是无法通过简单的Convention方式来做的,这时候就需要我们使用Configuration了。好在我们的Configuration是在代码中以&强类型&的方式来写的,比写繁琐易错的xml方式是要好的多了。&先来看看BookDto到Publisher的映射。
回顾一下前面中定义的规则:BookDto.Publisher -& Publisher.Name。
在AutoMapperzhong,我们可以这样映射:
var map = Mapper.CreateMap&BookDto,Publisher&();
map.ForMember(d =& d.Name, opt =& opt.MapFrom(s =& s.Publisher));
AutoMapper使用ForMember来指定每一个字段的映射规则:
The each custom member configuration uses an action delegate to configure each member.
还好有强大的lambda表达式,规则的定义简单明了。&此外,我们还可以使用ConstructUsing的方式一次直接定义好所有字段的映射规则。例如我们要定义BookDto到第一作者(Author)的ContactInfo的映射,使用ConstructUsing方式,我们可以:
var map = Mapper.CreateMap&BookDto,ContactInfo&();
map.ConstructUsing(s =& new ContactInfo
Blog = s.FirstAuthorBlog,
Email = s.FirstAuthorEmail,
Twitter = s.FirstAuthorTwitter
然后,就可以按照我们熟悉的方式来使用了:
BookDto dto = new BookDto
FirstAuthorEmail = "matt.",
FirstAuthorBlog = "",
ContactInfo contactInfo = Mapper.Map&BookDto, ContactInfo&(dto);
如果需要映射的2个类型有部分字段名称相同,又有部分字段名称不同呢?还好AutoMapper给我们提供的Convention或Configuration方式并不是&异或的&,我们可以结合使用两种方式,为名称不同的字段配置映射规则,而对于名称相同的字段则忽略配置。&例如对于前面提到的AddressDto到Address的映射,假如AddressDto的字段Country不叫Country叫CountryName,那么在写AddressDto到Address的映射规则时,只需要:
var map = Mapper.CreateMap&AddressDto, Address&();
map.ForMember(d =& d.Country, opt =& opt.MapFrom(s =& s.CountryName));
对于City、Street和PostCode无需定义任何规则,AutoMapper仍然可以帮我们进行正确的映射。
该文转自:/blog/1126219
阅读(...) 评论()2055人阅读
此功能是将客户端HTTP协议POST GET方式提交的数据转换为某个Model实例,对于客户端浏览器Ajax提交的键值对或json格式数据直接转换为Model类的实例;
废话不多说,直接贴代码:
/********************************************************************************
** 作者:Tyler
** 创始时间:
** 描述:通过js ajax 或 HTTP其他方式提交的GET,POST数据转换为指定的Model实例
*********************************************************************************/
using System.Collections.G
using System.L
using System.W
using System.Runtime.Serialization.J
using System.Web.Script.S
using System.IO;
using System.T
using System.Collections.S
namespace MyHttpRequest
public class RequestDataToCls
/// &summary&
/// Post提交JSON格式转换为实体类
/// &/summary&
/// &typeparam name=&T&&类型&/typeparam&
/// &param name=&myrequest&&Request对象&/param&
/// &returns&T&/returns&
public static T StramTomodelHttpPost&T&(HttpRequest myrequest)
byte[] byts = new byte[myrequest.InputStream.Length];
myrequest.InputStream.Read(byts, 0, byts.Length);
string jsonstr = System.Text.Encoding.Default.GetString(byts);
if (!String.IsNullOrEmpty(jsonstr))
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
JavaScriptSerializer jss = new JavaScriptSerializer();
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonstr)))
T jsonObject = (T)ser.ReadObject(ms);
return jsonO
catch (Exception ex)
throw new Exception(&Serialize Error: & + ex.Message);
throw new Exception(&Not KeyValue &);
/// &summary&
Post提交Form集合转换为实体类
/// &/summary&
/// &typeparam name=&T&&类型&/typeparam&
/// &param name=&myrequest&&Request对象&/param&
/// &returns&T&/returns&
public static T FormTomodelHttpPost&T&(HttpRequest myrequest)
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
NameValueCollection coll = myrequest.Form as NameValueC
IDictionary&string, object& idc = new Dictionary&string, object&();
foreach (string name in coll.Keys)
idc.Add(name, coll[name].ToString());
if (idc.Count & 0)
JavaScriptSerializer jss = new JavaScriptSerializer();
jsonstr = jss.Serialize(idc);
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonstr)))
T jsonObject = (T)ser.ReadObject(ms);
return jsonO
catch (Exception ex)
throw new Exception(&Serialize Error: & + ex.Message);
throw new Exception(&Not KeyValue &);
/// &summary&
/// Get提交JSON格式转换为实体类
/// &/summary&
/// &typeparam name=&T&&类型&/typeparam&
/// &param name=&myrequest&&Request对象&/param&
/// &returns&T&/returns&
public static T StramTomodelHttpGet&T&(string queryString)
string jsonstr = queryS
if (!String.IsNullOrEmpty(jsonstr))
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
JavaScriptSerializer jss = new JavaScriptSerializer();
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonstr)))
T jsonObject = (T)ser.ReadObject(ms);
return jsonO
catch (Exception ex)
throw new Exception(&Serialize Error: & + ex.Message);
throw new Exception(&Not KeyValue &);
/// &summary&
Get提交QueryString集合转换为实体类
/// &/summary&
/// &typeparam name=&T&&类型&/typeparam&
/// &param name=&myrequest&&Request对象&/param&
/// &returns&T&/returns&
public static T FormTomodelHttpGet&T&(HttpRequest myrequest)
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
NameValueCollection coll = myrequest.QueryString as NameValueC
IDictionary&string, object& idc = new Dictionary&string, object&();
foreach (string name in coll.Keys)
idc.Add(name, coll[name].ToString());
if (idc.Count & 0)
JavaScriptSerializer jss = new JavaScriptSerializer();
jsonstr = jss.Serialize(idc);
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonstr)))
T jsonObject = (T)ser.ReadObject(ms);
return jsonO
catch (Exception ex)
throw new Exception(&Serialize Error: & + ex.Message);
throw new Exception(&Not KeyValue &);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:70394次
排名:千里之外
转载:35篇
评论:10条
(1)(5)(5)(1)(8)(12)(4)(2)SpringMVC(3)
在编写可视化界面项目时,我们通常需要对数据进行类型转换、验证及格式化。
一、在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需要转换/格式化类似“”的数据,而OrderModel的orderDate需要转换/格式化类似“
15:11:13”的数据,因为大家都为java.util.Date类型,因此不太容易进行细粒度转换/格式化。
在Spring Web MVC环境中,数据类型转换、验证及格式化通常是这样使用的:
①、类型转换:首先表单数据(全部是字符串)通过WebDataBinder进行绑定到命令对象,内部通过PropertyEditor实现;
②:数据验证:在控制器中的功能处理方法中,需要显示的调用Spring的Validator实现并将错误信息添加到BindingResult对象中;
③:格式化显示:在表单页面可以通过如下方式展示通过PropertyEditor格式化的数据和错误信息:
Java代码&&
&%@taglib&prefix=&spring&&uri=&http://www.springframework.org/tags&&%&&&
&%@taglib&prefix=&form&&uri=&http://www.springframework.org/tags/form&&%&&&
&首先需要通过如上taglib指令引入spring的两个标签库。
Java代码&&
//1、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)&&
&spring:bind&path=&dataBinderTest.phoneNumber&&${status.value}&/spring:bind&&&
Java代码&&
//2、&spring:eval&标签,自动调用ConversionService并选择相应的Converter&SPI进行格式化展示&&
&spring:eval&expression=&dataBinderTest.phoneNumber&&&/spring:eval&&&
&如上代码能工作的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的作用是暴露conversionService到请求中以便如&spring:eval&标签使用。
Java代码&&
//3、通过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示&&
&form:form&commandName=&dataBinderTest&&&&
&&&&&form:input&path=&phoneNumber&/&&!--&如果出错会显示错误之前的数据而不是空&--&&&
&/form:form&&&
Java代码&&
//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、控制器定义:
Java代码&&
package&cn.javass.chapter7.web.&&
//省略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:
Java代码&&
&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-&date= 16:48:48&state=blocked
二、注解式控制器注册PropertyEditor:
1、使用WebDataBinder进行控制器级别注册PropertyEditor(控制器独享)
Java代码&&
@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,这样对于所有的注解式控制器都是共享的。
Java代码&&
&bean&class=&org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter&&&&
&&&&&property&name=&webBindingInitializer&&&&
&&&&&&&&&bean&class=&cn.javass.chapter7.web.controller.support.initializer.MyWebBindingInitializer&/&&&
&&&&&/property&&&
&此时我们注释掉控制器级别通过@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代码&&
package&org.springframework.core.convert.&&
public&interface&Converter&S,&T&&{&//①&S是源类型&T是目标类型&&
&&&&T&convert(S&source);&//②&转换S类型的source到T目标类型的转换方法&&
&示例:请参考cn.javass.chapter7.converter.support.StringToPhoneNumberConverter转换器,用于将String---&PhoneNumberModel。
此处我们可以看到Converter接口实现只能转换一种类型到另一种类型,不能进行多类型转换,如将一个数组转换成集合,如(String[] ----& List&String&、String[]-----&List&PhoneNumberModel&等)。
(2、GenericConverter和ConditionalGenericConverter:GenericConverter接口实现能在多种类型之间进行转换,ConditionalGenericConverter是有条件的在多种类型之间进行转换。
Java代码&&
package&org.springframework.core.convert.&&
public&interface&GenericConverter&{&&
&&&&Set&ConvertiblePair&&getConvertibleTypes();&&
&&&&Object&convert(Object&source,&TypeDescriptor&sourceType,&TypeDescriptor&targetType);&&
getConvertibleTypes:指定了可以转换的目标类型对;
convert:在sourceType和targetType类型之间进行转换。
Java代码&&
package&org.springframework.core.convert.&&
public&interface&ConditionalGenericConverter&extends&GenericConverter&{&&
&&&&boolean&matches(TypeDescriptor&sourceType,&TypeDescriptor&targetType);&&
&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代码&&
package&org.springframework.core.convert.&&
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---&Double, Byte----&Integer, Float---&Double等。
对于我们大部分用户来说一般不需要自定义ConverterFactory,如果需要可以参考内置的ConverterFactory来实现自己的。
2、类型转换器注册器、类型转换服务:提供类型转换器注册支持,运行时类型转换API支持。
&一共有如下两种接口:
(1、ConverterRegistry:类型转换器注册支持,可以注册/删除相应的类型转换器。
Java代码&&
package&org.springframework.core.convert.&&
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:运行时类型转换服务接口,提供运行期类型转换的支持。
Java代码&&
package&org.springframework.core.&&
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-----&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.3、示例
(1、自定义String-----&PhoneNumberModel的转换器
Java代码&&
package&cn.javass.chapter7.web.controller.support.&&
//省略import&&
public&class&StringToPhoneNumberConverter&implements&Converter&String,&PhoneNumberModel&&{&&
&&&&Pattern&pattern&=&pile(&^(\\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&phoneN&&
&&&&&&&&}&else&{&&
&&&&&&&&&&&&//③如果不匹配&转换失败&&
&&&&&&&&&&&&throw&new&IllegalArgumentException(String.format(&类型转换失败,需要格式[010-],但格式是[%s]&,&source));&&
&&&&&&&&}&&
String转换为Date的类型转换器,请参考cn.javass.chapter7.web.controller.support.converter.StringToDateConverter。
(2、测试用例(cn.javass.chapter7.web.controller.support.converter.ConverterTest)
Java代码&&
public&void&testStringToPhoneNumberConvert()&{&&
&&&&DefaultConversionService&conversionService&=&new&DefaultConversionService();&&
&&&&conversionService.addConverter(new&StringToPhoneNumberConverter());&&
&&&&String&phoneNumberStr&=&&010-&;&&
&&&&PhoneNumberModel&phoneNumber&=&conversionService.convert(phoneNumberStr,&PhoneNumberModel.class);&&
&&&&&&&&&&
&&&&Assert.assertEquals(&010&,&phoneNumber.getAreaCode());&&
&类似于PhoneNumberEditor将字符串“010-”转换为PhoneNumberModel。
Java代码&&
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实现和自定义的类型转换器
Java代码&&
&!--&①注册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&&&
&FormattingConversionServiceFactoryBean:是FactoryBean实现,默认使用DefaultFormattingConversionService转换器服务实现;
converters:注册我们自定义的类型转换器,此处注册了String---&PhoneNumberModel和String---&Date的类型转换器。
(2、通过ConfigurableWebBindingInitializer注册ConversionService
Java代码&&
&!--&②使用ConfigurableWebBindingInitializer注册conversionService&--&&&
&bean&id=&webBindingInitializer&&class=&org.springframework.web.bind.support.&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ConfigurableWebBindingInitializer&&&&
&&&&&property&name=&conversionService&&ref=&conversionService&/&&&
&此处我们通过ConfigurableWebBindingInitializer绑定初始化器进行ConversionService的注册;
3、注册ConfigurableWebBindingInitializer到RequestMappingHandlerAdapter
Java代码&&
&bean&class=&org.springframework.web.servlet.mvc.method.annotation.&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&RequestMappingHandlerAdapter&&&&
&property&name=&webBindingInitializer&&ref=&webBindingInitializer&/&&&
&通过如上配置,我们就完成了Spring3.0的类型转换系统与Spring Web MVC的集成。此时可以启动服务器输入之前的URL测试了。
此时可能有人会问,如果我同时使用PropertyEditor和ConversionService,执行顺序是什么呢?内部首先查找PropertyEditor进行类型转换,如果没有找到相应的PropertyEditor再通过ConversionService进行转换。
如上集成过程看起来比较麻烦,后边我们会介绍&mvc:annotation-driven&和@EnableWebMvc,ConversionService会自动注册,后续章节再详细介绍。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:17998次
排名:千里之外
原创:39篇
转载:38篇
(1)(1)(7)(1)(1)(2)(6)(3)(6)(8)(13)(6)(6)(11)(4)(5)(1)(1)(1)}

我要回帖

更多关于 微信怎么屏蔽群消息 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信