# 第四章 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、數據類型轉換和數據驗證

**流程:**
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=鎖定]
類型轉換如圖所示:

**四、注冊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架構進行自動識別,如圖所示:

此時我們把“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