# 第四章 Controller接口控制器详解(6)——跟着开涛学SpringMVC
### [第一章 Web MVC简介 —— 跟开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1593441 "第一章 Web MVC简介 —— 跟开涛学SpringMVC")
### [第二章 Spring MVC入门 —— 跟开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1594806 "第二章 Spring MVC入门 —— 跟开涛学SpringMVC ")
### [第三章 DispatcherServlet详解 ——跟开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1602617 "第三章 DispatcherServlet详解 ——跟开涛学SpringMVC")
### [第四章 Controller接口控制器详解(1)——跟着开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1608234 "第四章 Controller接口控制器详解(1)——跟着开涛学SpringMVC")
### [第四章 Controller接口控制器详解(2)——跟着开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1611743 "第四章 Controller接口控制器详解(2)——跟着开涛学SpringMVC")
### [第四章 Controller接口控制器详解(3)——跟着开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1622684 "第四章 Controller接口控制器详解(3)——跟着开涛学SpringMVC")
### [第四章 Controller接口控制器详解(4)——跟着开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1629956 "第四章 Controller接口控制器详解(4)——跟着开涛学SpringMVC")
### [第四章 Controller接口控制器详解(5)——跟着开涛学SpringMVC](http://jinnianshilongnian.iteye.com/blog/1630585 "第四章 Controller接口控制器详解(5)——跟着开涛学SpringMVC")
### [第四章 Controller接口控制器详解(6)——跟着开涛学SpringMVC](http://www.iteye.com/blogs/subjects/records/1594 "第四章 Controller接口控制器详解(6)——跟着开涛学SpringMVC")
## 4.16、数据类型转换和数据验证
![](https://box.kancloud.cn/2016-05-15_5737edd82363e.JPG)
**流程:**
1、首先创建数据绑定器,在此此会创建ServletRequestDataBinder类的对象,并设置messageCodesResolver(错误码解析器);
2、提供第一个扩展点,初始化数据绑定器,在此处我们可以覆盖该方法注册自定义的PropertyEditor(请求参数——>命令对象属性的转换);
3、进行数据绑定,即请求参数——>命令对象的绑定;
4、提供第二个扩展点,数据绑定完成后的扩展点,此处可以实现一些自定义的绑定动作;
5、验证器对象的验证,验证器通过validators注入,如果验证失败,需要把错误信息放入Errors(此处使用BindException实现);
6、提供第三个扩展点,此处可以实现自定义的绑定/验证逻辑;
7、将errors传入功能处理方法进行处理,功能方法应该判断该错误对象是否有错误进行相应的处理。
### 4.16.1、数据类型转换
请求参数(String)——>命令对象属性(可能是任意类型)的类型转换,即数据绑定时的类型转换,使用PropertyEditor实现绑定时的类型转换。
**一、Spring内建的PropertyEditor如下所示:**
| **类名** | **说明** | **默认是否注册** |
| --- | --- | --- |
| ByteArrayPropertyEditor | String<——>byte[] | √ |
| ClassEditor | String<——>Class当类没有发现抛出`IllegalArgumentException` | √ |
| CustomBooleanEditor | String<——>Booleantrue/yes/on/1转换为true,false/no/off/0转换为false | √ |
| CustomCollectionEditor | 数组/Collection——>Collection普通值——>Collection(只包含一个对象)如String——>Collection不允许Collection——>String(单方向转换) | √ |
| CustomNumberEditor | String<——>Number(Integer、Long、Double) | √ |
| FileEditor | String<——>File | √ |
| InputStreamEditor | String——>InputStream单向的,不能InputStream——>String | √ |
| LocaleEditor | String<——>Locale,(String的形式为[语言]_[国家]_[变量],这与Local对象的toString()方法得到的结果相同) | √ |
| PatternEditor | String<——>Pattern | √ |
| PropertiesEditor | String<——>java.lang.Properties | √ |
| URLEditor | String<——>URL | √ |
| StringTrimmerEditor | 一个用于trim 的 String类型的属性编辑器如默认删除两边的空格,charsToDelete属性:可以设置为其他字符emptyAsNull属性:将一个空字符串转化为null值的选项。 | × |
| CustomDateEditor | String<——>java.util.Date | × |
**二、Spring内建的PropertyEditor支持的属性(符合JavaBean规范)操作:**
| **表达式** | **设值/取值说明** |
| --- | --- | --- |
| username | 属性username设值方法setUsername()/取值方法getUsername() 或 isUsername() |
| schooInfo.schoolType | 属性schooInfo的嵌套属性schoolType设值方法getSchooInfo().setSchoolType()/取值方法getSchooInfo().getSchoolType() |
| hobbyList[0] | 属性hobbyList的第一个元素索引属性可能是一个数组、列表、其它天然有序的容器。 |
| map[key] | 属性map(java.util.Map类型)map中key对应的值 |
**三、示例:**
接下来我们写自定义的属性编辑器进行数据绑定:
**(1、模型对象:**
```
package cn.javass.chapter4.model;
//省略import
public class DataBinderTestModel {
private String username;
private boolean bool;//Boolean值测试
private SchoolInfoModel schooInfo;
private List hobbyList;//集合测试,此处可以改为数组/Set进行测试
private Map map;//Map测试
private PhoneNumberModel phoneNumber;//String->自定义对象的转换测试
private Date date;//日期类型测试
private UserState state;//String——>Enum类型转换测试
//省略getter/setter
}
package cn.javass.chapter4.model;
//如格式010-12345678
public class PhoneNumberModel {
private String areaCode;//区号
private String phoneNumber;//电话号码
//省略getter/setter
}
```
**(2、PhoneNumber属性编辑器**
前台输入如010-12345678自动转换为PhoneNumberModel。
```
package cn.javass.chapter4.web.controller.support.editor;
//省略import
public class PhoneNumberEditor extends PropertyEditorSupport {
Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");
@Override
public void setAsText(String text) throws IllegalArgumentException {
if(text == null || !StringUtils.hasLength(text)) {
setValue(null); //如果没值,设值为null
}
Matcher matcher = pattern.matcher(text);
if(matcher.matches()) {
PhoneNumberModel phoneNumber = new PhoneNumberModel();
phoneNumber.setAreaCode(matcher.group(1));
phoneNumber.setPhoneNumber(matcher.group(2));
setValue(phoneNumber);
} else {
throw new IllegalArgumentException(String.format("类型转换失败,需要格式[010-12345678],但格式是[%s]", text));
}
}
@Override
public String getAsText() {
PhoneNumberModel phoneNumber = ((PhoneNumberModel)getValue());
return phoneNumber == null ? "" : phoneNumber.getAreaCode() + "-" + phoneNumber.getPhoneNumber();
}
}
```
**PropertyEditorSupport:**一个PropertyEditor的支持类;
**setAsText:**表示将String——>PhoneNumberModel,根据正则表达式进行转换,如果转换失败抛出异常,则接下来的验证器会进行验证处理;
**getAsText:**表示将PhoneNumberModel——>String。
**(3、控制器**
需要在控制器注册我们自定义的属性编辑器。
此处我们使用AbstractCommandController,因为它继承了BaseCommandController,拥有绑定流程。
```
package cn.javass.chapter4.web.controller;
//省略import
public class DataBinderTestController extends AbstractCommandController {
public DataBinderTestController() {
setCommandClass(DataBinderTestModel.class); //设置命令对象
setCommandName("dataBinderTest");//设置命令对象的名字
}
@Override
protected ModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Object command, BindException errors) throws Exception {
//输出command对象看看是否绑定正确
System.out.println(command);
return new ModelAndView("bindAndValidate/success").addObject("dataBinderTest", command);
}
@Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
super.initBinder(request, binder);
//注册自定义的属性编辑器
//1、日期
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
//表示如果命令对象有Date类型的属性,将使用该属性编辑器进行类型转换
binder.registerCustomEditor(Date.class, dateEditor);
//自定义的电话号码编辑器
binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEditor());
}
}
```
**initBinder:**第一个扩展点,初始化数据绑定器,在此处我们注册了两个属性编辑器;
**CustomDateEditor:**自定义的日期编辑器,用于在String<——>日期之间转换;
binder.registerCustomEditor(Date.**class**, dateEditor):表示如果命令对象是Date类型,则使用dateEditor进行类型转换;
**PhoneNumberEditor:**自定义的电话号码属性编辑器用于在String<——> PhoneNumberModel之间转换;
binder.registerCustomEditor(PhoneNumberModel.**class**, **new**PhoneNumberEditor()):表示如果命令对象是PhoneNumberModel类型,则使用PhoneNumberEditor进行类型转换;
`**(4、spring配置文件chapter4-servlet.xml**`
```
<bean name="/dataBind"
class="cn.javass.chapter4.web.controller.DataBinderTestController"/>
```
**(5、视图页面(WEB-INF/jsp/bindAndValidate/success.jsp)**
```
EL phoneNumber:${dataBinderTest.phoneNumber}<br/>
EL state:${dataBinderTest.state}<br/>
EL date:${dataBinderTest.date}<br/>
```
视图页面的数据没有预期被格式化,如何进行格式化显示呢?请参考【第七章 注解式控制器的数据验证、类型转换及格式化】。
**(6、测试:**
**1、在浏览器地址栏输入请求的URL,如**
http://localhost:9080/springmvc-chapter4/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、控制器输出的内容:**
DataBinderTestModel [username=zhang, bool=true, schooInfo=SchoolInfoModel [schoolType=null, schoolName=null, specialty=computer], hobbyList=[program, music], map={key1=value1, key2=value2}, phoneNumber=PhoneNumberModel [areaCode=010, phoneNumber=12345678], date=Sun Mar 18 16:48:48 CST 2012, state=锁定]
类型转换如图所示:
![](https://box.kancloud.cn/2016-05-15_5737edd8424b3.JPG)
**四、注册PropertyEditor**
**1、使用WebDataBinder进行控制器级别注册PropertyEditor(控制器独享)**
如“【三、示例】”中所使用的方式,使用WebDataBinder注册控制器级别的PropertyEditor,这种方式注册的PropertyEditor只对当前控制器独享,即其他的控制器不会自动注册这个PropertyEditor,如果需要还需要再注册一下。
**2、使用**`**WebBindingInitializer批量注册**`**PropertyEditor**
如果想在多个控制器同时注册多个相同的PropertyEditor时,可以考虑使用WebBindingInitializer。
示例:
(1、实现WebBindingInitializer
```
package cn.javass.chapter4.web.controller.support.initializer;
//省略import
public class MyWebBindingInitializer implements WebBindingInitializer {
@Override
public void initBinder(WebDataBinder binder, WebRequest request) {
//注册自定义的属性编辑器
//1、日期
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
//表示如果命令对象有Date类型的属性,将使用该属性编辑器进行类型转换
binder.registerCustomEditor(Date.class, dateEditor);
//自定义的电话号码编辑器
binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEditor());
}
}
```
通过实现WebBindingInitializer并通过binder注册多个PropertyEditor。
(2、修改【三、示例】中的DataBinderTestController,注释掉initBinder方法;
(3、修改chapter4-servlet.xml配置文件:
```
<!-- 注册WebBindingInitializer实现 -->
<bean id="myWebBindingInitializer" class="cn.javass.chapter4.web.controller.support.initializer.MyWebBindingInitializer"/>
<bean name="/dataBind" class="cn.javass.chapter4.web.controller.DataBinderTestController">
<!-- 注入WebBindingInitializer实现 -->
<property name="webBindingInitializer" ref="myWebBindingInitializer"/>
</bean>
```
(4、尝试访问“【三、示例】”中的测试URL即可成功。
使用WebBindingInitializer的好处是当你需要在多个控制器中需要同时使用多个相同的PropertyEditor可以在WebBindingInitializer实现中注册,这样只需要在控制器中注入WebBindingInitializer即可注入多个PropertyEditor。
**3、全局级别注册PropertyEditor(全局共享)**
只需要将我们自定义的PropertyEditor放在和你的模型类同包下即可,且你的Editor命名规则必须是“模型类名Editor”,这样Spring会自动使用标准JavaBean架构进行自动识别,如图所示:
![](https://box.kancloud.cn/2016-05-15_5737edd85a7d3.JPG)
此时我们把“DataBinderTestController”的“binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEditor());”注释掉,再尝试访问“【三、示例】”中的测试URL即可成功。
这种方式不仅仅在使用Spring时可用,在标准的JavaBean等环境都是可用的,可以认为是全局共享的(不仅仅是Spring环境)。
**PropertyEditor被限制为只能String<——>Object之间转换,不能Object<——>Object,Spring3提供了更强大的类型转换(Type**`**Conversion)支持,它可以在任意对象之间进行类型转换,不仅仅是String**`**<**`**——>Object。**`
如果我在地址栏输入错误的数据,即数据绑定失败,Spring Web MVC该如何处理呢?如果我输入的数据不合法呢?如用户名输入100个字符(超长了)那又该怎么处理呢?出错了需要错误消息,那错误消息应该是硬编码?还是可配置呢?
接下来我们来学习一下数据验证器进行数据验证吧。
[私塾在线学习网](http://sishuok.com/)原创内容([http://sishuok.com](http://sishuok.com/))
原创内容,转载请注明私塾在线【[http://sishuok.com/forum/blogPost/list/5677.html](http://sishuok.com/forum/blogPost/list/0/5514.html)】
- 跟我学 Spring3
- 【第二章】 IoC 之 2.1 IoC基础 ——跟我学Spring3
- 【第二章】 IoC 之 2.2 IoC 容器基本原理 ——跟我学Spring3
- 【第二章】 IoC 之 2.3 IoC的配置使用——跟我学Spring3
- 【第三章】 DI 之 3.1 DI的配置使用 ——跟我学spring3
- 【第三章】 DI 之 3.2 循环依赖 ——跟我学spring3
- 【第三章】 DI 之 3.3 更多DI的知识 ——跟我学spring3
- 【第三章】 DI 之 3.4 Bean的作用域 ——跟我学spring3
- 【第四章】 资源 之 4.1 基础知识 ——跟我学spring3
- 【第四章】 资源 之 4.2 内置Resource实现 ——跟我学spring3
- 【第四章】 资源 之 4.3 访问Resource ——跟我学spring3
- 【第四章】 资源 之 4.4 Resource通配符路径 ——跟我学spring3
- 【第五章】Spring表达式语言 之 5.1 概述 5.2 SpEL基础 ——跟我学spring3
- 【第五章】Spring表达式语言 之 5.3 SpEL语法 ——跟我学spring3
- 【第五章】Spring表达式语言 之 5.4在Bean定义中使用EL—跟我学spring3
- 【第六章】 AOP 之 6.1 AOP基础 ——跟我学spring3
- 【第六章】 AOP 之 6.2 AOP的HelloWorld ——跟我学spring3
- 【第六章】 AOP 之 6.3 基于Schema的AOP ——跟我学spring3
- 【第六章】 AOP 之 6.4 基于@AspectJ的AOP ——跟我学spring3
- 【第六章】 AOP 之 6.5 AspectJ切入点语法详解 ——跟我学spring3
- 【第六章】 AOP 之 6.6 通知参数 ——跟我学spring3
- 【第六章】 AOP 之 6.7 通知顺序 ——跟我学spring3
- 【第六章】 AOP 之 6.8 切面实例化模型 ——跟我学spring3
- 【第六章】 AOP 之 6.9 代理机制 ——跟我学spring3
- 【第七章】 对JDBC的支持 之 7.1 概述 ——跟我学spring3
- 【第七章】 对JDBC的支持 之 7.2 JDBC模板类 ——跟我学spring3
- 【第七章】 对JDBC的支持 之 7.3 关系数据库操作对象化 ——跟我学spring3
- 【第七章】 对JDBC的支持 之 7.4 Spring提供的其它帮助 ——跟我学spring3【私塾在线原创】
- 【第七章】 对JDBC的支持 之 7.5 集成Spring JDBC及最佳实践 ——跟我学spring3
- 【第八章】 对ORM的支持 之 8.1 概述 ——跟我学spring3
- 【第八章】 对ORM的支持 之 8.2 集成Hibernate3 ——跟我学spring3
- 【第八章】 对ORM的支持 之 8.3 集成iBATIS ——跟我学spring3
- 【第八章】 对ORM的支持 之 8.4 集成JPA ——跟我学spring3
- 【第九章】 Spring的事务 之 9.1 数据库事务概述 ——跟我学spring3
- 【第九章】 Spring的事务 之 9.2 事务管理器 ——跟我学spring3
- 【第九章】 Spring的事务 之 9.3 编程式事务 ——跟我学spring3
- 【第九章】 Spring的事务 之 9.4 声明式事务 ——跟我学spring3
- 【第十章】集成其它Web框架 之 10.1 概述 ——跟我学spring3
- 【第十章】集成其它Web框架 之 10.2 集成Struts1.x ——跟我学spring3
- 【第十章】集成其它Web框架 之 10.3 集成Struts2.x ——跟我学spring3
- 【第十章】集成其它Web框架 之 10.4 集成JSF ——跟我学spring3
- 【第十一章】 SSH集成开发积分商城 之 11.1 概述 ——跟我学spring3
- 【第十一章】 SSH集成开发积分商城 之 11.2 实现通用层 ——跟我学spring3
- 【第十一章】 SSH集成开发积分商城 之 11.3 实现积分商城层 ——跟我学spring3
- 【第十二章】零配置 之 12.1 概述 ——跟我学spring3
- 【第十二章】零配置 之 12.2 注解实现Bean依赖注入 ——跟我学spring3
- 【第十二章】零配置 之 12.3 注解实现Bean定义 ——跟我学spring3
- 【第十二章】零配置 之 12.4 基于Java类定义Bean配置元数据 ——跟我学spring3
- 【第十二章】零配置 之 12.5 综合示例-积分商城 ——跟我学spring3
- 【第十三章】 测试 之 13.1 概述 13.2 单元测试 ——跟我学spring3
- 【第十三章】 测试 之 13.3 集成测试 ——跟我学spring3
- 跟我学 Spring MVC
- SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结
- Spring Web MVC中的页面缓存支持 ——跟我学SpringMVC系列
- Spring3 Web MVC下的数据类型转换(第一篇)——《跟我学Spring3 Web MVC》抢先看
- Spring3 Web MVC下的数据格式化(第二篇)——《跟我学Spring3 Web MVC》抢先看
- 第一章 Web MVC简介 —— 跟开涛学SpringMVC
- 第二章 Spring MVC入门 —— 跟开涛学SpringMVC
- 第三章 DispatcherServlet详解 ——跟开涛学SpringMVC
- 第四章 Controller接口控制器详解(1)——跟着开涛学SpringMVC
- 第四章 Controller接口控制器详解(2)——跟着开涛学SpringMVC
- 第四章 Controller接口控制器详解(3)——跟着开涛学SpringMVC
- 第四章 Controller接口控制器详解 (4)——跟着开涛学SpringMVC
- 第四章 Controller接口控制器详解(5)——跟着开涛学SpringMVC
- 跟着开涛学SpringMVC 第一章源代码下载
- 第二章 Spring MVC入门 源代码下载
- 第四章 Controller接口控制器详解 源代码下载
- 第四章 Controller接口控制器详解(6)——跟着开涛学SpringMVC
- 第四章 Controller接口控制器详解(7 完)——跟着开涛学SpringMVC
- 第五章 处理器拦截器详解——跟着开涛学SpringMVC
- 源代码下载 第五章 处理器拦截器详解——跟着开涛学SpringMVC
- 注解式控制器运行流程及处理器定义 第六章 注解式控制器详解——跟着开涛学SpringMVC
- 源代码下载 第六章 注解式控制器详解
- SpringMVC3强大的请求映射规则详解 第六章 注解式控制器详解——跟着开涛学SpringMVC
- Spring MVC 3.1新特性 生产者、消费者请求限定 —— 第六章 注解式控制器详解——跟着开涛学SpringMVC
- SpringMVC强大的数据绑定(1)——第六章 注解式控制器详解——跟着开涛学SpringMVC
- SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解——跟着开涛学SpringMVC
- SpringMVC数据类型转换——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC
- SpringMVC数据格式化——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC
- SpringMVC数据验证——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC