第四章 Controller接口控制器詳解(6)

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<——>Boolean

true/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、模型對象:


java代碼:
  1. package cn.javass.chapter4.model;  
  2. //省略import  
  3. public class DataBinderTestModel {  
  4.     private String username;  
  5.     private boolean bool;//Boolean值測試  
  6.     private SchoolInfoModel schooInfo;  
  7.     private List hobbyList;//集合測試,此處可以改爲數組/Set進行測試  
  8.     private Map map;//Map測試  
  9.     private PhoneNumberModel phoneNumber;//String->自定義對象的轉換測試  
  10.     private Date date;//日期類型測試  
  11.     private UserState state;//String——>Enum類型轉換測試  
  12.     //省略getter/setter  
  13. }  
  14.   
  15. package cn.javass.chapter4.model;  
  16. //如格式010-12345678  
  17. public class PhoneNumberModel {  
  18.     private String areaCode;//區號  
  19.     private String phoneNumber;//電話號碼  
  20.     //省略getter/setter  
  21. }  

(2、PhoneNumber屬性編輯器

前臺輸入如010-12345678自動轉換爲PhoneNumberModel。

 

java代碼:
  1. package cn.javass.chapter4.web.controller.support.editor;  
  2. //省略import  
  3. public class PhoneNumberEditor extends PropertyEditorSupport {  
  4.     Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");  
  5.     @Override  
  6.     public void setAsText(String text) throws IllegalArgumentException {  
  7.         if(text == null || !StringUtils.hasLength(text)) {  
  8.             setValue(null); //如果沒值,設值爲null  
  9.         }  
  10.         Matcher matcher = pattern.matcher(text);  
  11.         if(matcher.matches()) {  
  12.             PhoneNumberModel phoneNumber = new PhoneNumberModel();  
  13.             phoneNumber.setAreaCode(matcher.group(1));  
  14.             phoneNumber.setPhoneNumber(matcher.group(2));  
  15.             setValue(phoneNumber);  
  16.         } else {  
  17.             throw new IllegalArgumentException(String.format("類型轉換失敗,需要格式[010-12345678],但格式是[%s]", text));  
  18.         }  
  19.     }  
  20.     @Override  
  21.     public String getAsText() {  
  22.         PhoneNumberModel phoneNumber = ((PhoneNumberModel)getValue());  
  23.         return phoneNumber == null ? "" : phoneNumber.getAreaCode() + "-" + phoneNumber.getPhoneNumber();  
  24.     }  
  25. }  

PropertyEditorSupport:一個PropertyEditor的支持類;

setAsText:表示將String——>PhoneNumberModel,根據正則表達式進行轉換,如果轉換失敗拋出異常,則接下來的驗證器會進行驗證處理;

getAsText:表示將PhoneNumberModel——>String。

 

(3、控制器

需要在控制器註冊我們自定義的屬性編輯器。

此處我們使用AbstractCommandController,因爲它繼承了BaseCommandController,擁有綁定流程。

 

java代碼:
  1. package cn.javass.chapter4.web.controller;  
  2. //省略import  
  3. public class DataBinderTestController extends AbstractCommandController {  
  4.     public DataBinderTestController() {  
  5.         setCommandClass(DataBinderTestModel.class); //設置命令對象  
  6.         setCommandName("dataBinderTest");//設置命令對象的名字  
  7.     }  
  8.     @Override  
  9.     protected ModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Object command, BindException errors) throws Exception {  
  10.         //輸出command對象看看是否綁定正確  
  11.         System.out.println(command);  
  12.         return new ModelAndView("bindAndValidate/success").addObject("dataBinderTest", command);  
  13.     }  
  14.     @Override  
  15.     protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {  
  16.         super.initBinder(request, binder);  
  17.         //註冊自定義的屬性編輯器  
  18.         //1、日期  
  19.         DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  20.         CustomDateEditor dateEditor = new CustomDateEditor(df, true);  
  21.         //表示如果命令對象有Date類型的屬性,將使用該屬性編輯器進行類型轉換  
  22.         binder.registerCustomEditor(Date.class, dateEditor);  
  23.         //自定義的電話號碼編輯器  
  24.         binder.registerCustomEditor(PhoneNumberModel.classnew PhoneNumberEditor());  
  25.     }  
  26. }  

initBinder:第一個擴展點,初始化數據綁定器,在此處我們註冊了兩個屬性編輯器;

CustomDateEditor:自定義的日期編輯器,用於在String<——>日期之間轉換;

    binder.registerCustomEditor(Date.class, dateEditor):表示如果命令對象是Date類型,則使用dateEditor進行類型轉換;

PhoneNumberEditor:自定義的電話號碼屬性編輯器用於在String<——> PhoneNumberModel之間轉換;

    binder.registerCustomEditor(PhoneNumberModel.classnew PhoneNumberEditor()):表示如果命令對象是PhoneNumberModel類型,則使用PhoneNumberEditor進行類型轉換;

(4、spring配置文件chapter4-servlet.xml


java代碼:
  1. <bean name="/dataBind"   
  2. class="cn.javass.chapter4.web.controller.DataBinderTestController"/>  

(5、視圖頁面(WEB-INF/jsp/bindAndValidate/success.jsp)


java代碼:
  1. EL phoneNumber:${dataBinderTest.phoneNumber}<br/>  
  2. EL state:${dataBinderTest.state}<br/>  
  3. 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


java代碼:
  1. package cn.javass.chapter4.web.controller.support.initializer;  
  2. //省略import  
  3. public class MyWebBindingInitializer implements WebBindingInitializer {  
  4.     @Override  
  5.     public void initBinder(WebDataBinder binder, WebRequest request) {  
  6.         //註冊自定義的屬性編輯器  
  7.         //1、日期  
  8.         DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  9.         CustomDateEditor dateEditor = new CustomDateEditor(df, true);  
  10.         //表示如果命令對象有Date類型的屬性,將使用該屬性編輯器進行類型轉換  
  11.         binder.registerCustomEditor(Date.class, dateEditor);  
  12.         //自定義的電話號碼編輯器  
  13.         binder.registerCustomEditor(PhoneNumberModel.classnew PhoneNumberEditor());  
  14.     }  
  15. }  

通過實現WebBindingInitializer並通過binder註冊多個PropertyEditor。

 

(2、修改【三、示例】中的DataBinderTestController,註釋掉initBinder方法;

 

(3、修改chapter4-servlet.xml配置文件:

 

java代碼:
  1. <!-- 註冊WebBindingInitializer實現 -->  
  2. <bean id="myWebBindingInitializer" class="cn.javass.chapter4.web.controller.support.initializer.MyWebBindingInitializer"/>  
  3. <bean name="/dataBind" class="cn.javass.chapter4.web.controller.DataBinderTestController">  
  4.     <!-- 注入WebBindingInitializer實現 -->  
  5.     <property name="webBindingInitializer" ref="myWebBindingInitializer"/>  
  6. </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/forum/blogPost/list/5677.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章