1. 程式人生 > 實用技巧 >【SpringMVC】資料轉換 & 資料格式化 & 資料校驗

【SpringMVC】資料轉換 & 資料格式化 & 資料校驗

資料轉換 & 資料格式化 & 資料校驗

資料繫結流程

  1. Spring MVC 主框架將 ServletRequest 物件及目標方法的入參例項傳遞給 WebDataBinderFactory 例項,以建立 DataBinder 例項物件
  2. DataBinder 呼叫裝配在 Spring MVC 上下文中的 ConversionService 元件進行資料型別轉換、資料格式化工作。將 Servlet 中的請求資訊填充到入參物件中
  3. 呼叫 Validator 元件對已經綁定了請求訊息的入參物件進行資料合法性校驗,並最終生成資料繫結結果 BindingData 物件
  4. Spring MVC 抽取 BindingResult 中的入參物件和校驗錯誤物件,將它們賦給處理方法的響應入參

Spring MVC 通過反射機制對目標處理方法進行解析,將請求訊息繫結到處理方法的入參中。資料繫結的核心部件是 DataBinder,執行機制如下:

資料轉換

  • Spring MVC 上下文中內建了很多轉換器,可完成大多數 Java 型別的轉換工作。
  • ConversionService converters =
    • java.lang.Boolean -> java.lang.String :
      org.springframework.core.convert.support.ObjectToStringConverter@f874ca
    • java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9
    • java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961
    • java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a
    • java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5
    • java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f
    • java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8
    • java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626
    • java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800
    • java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e
    • java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12
    • java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1
    • java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828
    • java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23
    • java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a
    • java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f
    • java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f
    • ……

自定義型別轉換器

  • ConversionService 是 Spring 型別轉換體系的核心介面。
  • 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定義一個 ConversionService. Spring 將自動識別出 IOC 容器中的 ConversionService,並在 Bean 屬性配置及 Spring MVC 處理方法入參繫結等場合使用它進行資料的轉換
  • 可通過 ConversionServiceFactoryBean 的 converters 屬性註冊自定義的型別轉換器
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
  <property name="converters">
    <list>
      <bean class="com.nemo.springmvc.UserConverter"></bean>
    </list>
  </property>
</bean>

Spring 支援的轉換器

Spring 定義了 3 種類型的轉換器介面,實現任意一個轉換器介面都可以作為自定義轉換器註冊到 ConversionServiceFactroyBean 中:

  • Converter<S,T>:將 S 型別物件轉為 T 型別物件
  • ConverterFactory:將相同系列多個 “同質” Converter 封裝在一起。如果希望將一種型別的物件轉換為另一種型別及其子類的物件(例如將 String 轉換為 Number 及 Number 子類 (Integer、Long、Double 等)物件)可使用該轉換器工廠類
  • GenericConverter:會根據源類物件及目標類物件所在的宿主類 中的上下文資訊進行型別轉換

自定義轉換器示例

<mvc:annotation-driven conversion-service=“conversionService”/> 會將自定義的 ConversionService 註冊到 Spring MVC 的上下文中

<mvc:annotation-driven conversion-ervice="conversionService"></mvc:annotation-driven>

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
  <property name="converters">
    <list>
      <bean class="com.nemo.springmvc.UserConverter"></bean>
    </list>
  </property>
</bean>
@RequestMapping("/handle24")
public String handle24(@RequestParam("user") User user) {
    System.out.println(user);
    return "success";
}

關於 mvc:annotation-driven

  • <mvc:annotation-driven/> 會自動註冊RequestMappingHandlerMapping、RequestMappingHandlerAdapter 與 ExceptionHandlerExceptionResolver 三個bean。
  • 還將提供以下支援:
    • 支援使用 ConversionService 例項對錶單引數進行型別轉換
    • 支援使用 @NumberFormat annotation、@DateTimeFormat 註解完成資料型別的格式化
    • 支援使用 @Valid 註解對 JavaBean 例項進行 JSR 303 驗證
    • 支援使用 @RequestBody 和 @ResponseBody 註解

既沒有配置 <mvc:default-servlet-handler/> 也沒有配置 <mvc:annotation-driven/>

配置了 <mvc:default-servlet-handler/> 但沒有配置 <mvc:annotation-driven/>

既配置了 <mvc:default-servlet-handler/> 又配置 <mvc:annotation-driven/>

@InitBinder

  • 由 @InitBinder 標識的方法,可以對 WebDataBinder 物件進行初始化。WebDataBinder 是 DataBinder 的子類,用於完成由表單欄位到 JavaBean 屬性的繫結
  • @InitBinder方法不能有返回值,它必須宣告為void。
  • @InitBinder方法的引數通常是是 WebDataBinder
/**
 * 不自動繫結物件中的 roleSet 屬性,另行處理。
 */
@InitBinder
public void intBinder(WebDataBinder dataBinder) {
    dataBinder.setDisallowFields("roleSet");
}

資料格式化

  • 對屬性物件的輸入/輸出進行格式化,從其本質上講依然屬於 “型別轉換” 的範疇。
  • Spring 在格式化模組中定義了一個實現 ConversionService 介面的 FormattingConversionService 實現類,該實現類擴充套件了 GenericConversionService,因此它既具有型別轉換的功能,又具有格式化的功能
  • FormattingConversionService 擁有一個 FormattingConversionServiceFactroyBean 工廠類,後者用於在 Spring 上下文中構造前者
  • FormattingConversionServiceFactroyBean 內部已經註冊了:
    • NumberFormatAnnotationFormatterFactroy:支援對數字型別的屬性使用 @NumberFormat 註解
    • JodaDateTimeFormatAnnotationFormatterFactroy:支援對日期型別的屬性使用 @DateTimeFormat 註解
  • 裝配了 FormattingConversionServiceFactroyBean 後,就可以在 Spring MVC 入參繫結及模型資料輸出時使用註解驅動了。
    <mvc:annotation-driven/> 預設建立的 ConversionService 例項即為 FormattingConversionServiceFactroyBean

日期格式化

@DateTimeFormat 註解可對 java.util.Date、java.util.Calendar、java.long.Long 時間型別進行標註:

  • pattern 屬性:型別為字串。指定解析/格式化欄位資料的模式,如:”yyyy-MM-dd hh:mm:ss”
  • iso 屬性:型別為 DateTimeFormat.ISO。指定解析/格式化欄位資料的ISO模式,包括四種:ISO.NONE(不使用) -- 默 認、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)
  • style 屬性:字串型別。通過樣式指定日期時間的格式,由兩位字元組成,第一位表示日期的格式,第二位表示時間的格式:S:短日期/時間格式、M:中日期/時間格式、L:長日期/時間格式、F:完整日期/時間格式、-:忽略日期或時間格式

數值格式化

@NumberFormat 可對類似數字型別的屬性進行標註,它擁有兩個互斥的屬性:

  • style:型別為 NumberFormat.Style。用於指定樣式型別,包括三種:Style.NUMBER(正常數字型別)、Style.CURRENCY(貨幣型別)、 Style.PERCENT(百分數型別)
  • pattern:型別為 String,自定義樣式,如patter="#,###";

格式化示例

<mvc:annotation-driven></mvc:annotation-driven>
public class User {
    @DateTimeFormat(pattern="yyyy/MM/dd")
    private Date birthday;

    @NumberFormat(pattern="#,###.###")
    private Double wages;
}
@RequestMapping("/handle19")
public String handle19(@ModelAttribute("user") User user) {
    user.setId(1000);
    System.out.println(user);
    return "success";
}

資料校驗

JSR 303

  • JSR 303 是 Java 為 Bean 資料合法性校驗提供的標準框架,它已經包含在 JavaEE 6.0 中
  • JSR 303 通過在 Bean 屬性上標註類似於 @NotNull、@Max 等標準的註解指定校驗規則,並通過標準的驗證介面對 Bean 進行驗證
註解 功能說明
@Null 被註解的元素必須為null
@NotNull 被註解的元素必須不為null
@AssertTure 被註解的元素必須為true
@AssertFalse 被註解的元素必須為false
@Min(value) 被註解的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value) 被註解的元素必須是一個數字其值必須小於等於指定的最大值
@DecimalMin(value) 被註解的元素必須是一個數字其值必須大於等於指定的最小值
@DecimalMax(value) 被註解的元素必須是一個數字其值必須小於等於指定的最大值
@Size(max,min) 被註解的元素的大小必須在指定的範圍內
@Digits(integer,fraction) 被註解的元素必須是一個數字,其值必須在可接受的範圍內
@Past 被註解的元素必須是一個過去的日期
@Future 被註解的元素必須是一個將來的日期
@Pattern(value) 被註解的元素必須符合指定的正則表示式

Hibernate Validator 擴充套件註解

Hibernate Validator 是 JSR 303 的一個參考實現,除支援所有標準的校驗註解外,它還支援以下的擴充套件註解

註解 功能說明
@Email 被註解的元素必須是電子郵箱地址
@Length 被註解的字串的大小必須在指定的範圍內
@NotEmpty 被註解的字串必須非空
@Range 被註解的元素必須在合適的範圍內

Spring MVC 資料校驗

  • Spring 4.0 擁有自己獨立的資料校驗框架,同時支援 JSR 303 標準的校驗框架。
  • Spring 在進行資料繫結時,可同時呼叫校驗框架完成資料校驗工作。在 Spring MVC 中,可直接通過註解驅動的方式進行資料校驗
  • Spring 的 LocalValidatorFactroyBean 既實現了 Spring 的 Validator 介面,也實現了 JSR 303 的 Validator 介面。只要在 Spring 容器中定義了一個 LocalValidatorFactoryBean,即可將其注入到需要資料校驗的 Bean 中。
  • Spring 本身並沒有提供 JSR303 的實現,所以必須將 JSR303 的實現者的 jar 包放到類路徑下。

  • <mvc:annotation-driven/> 會預設裝配好一個 LocalValidatorFactoryBean,通過在處理方法的入參上標 注 @valid 註解即可讓 Spring MVC 在完成資料繫結後執行資料校驗的工作
  • 在已經標註了 JSR303 註解的表單/命令物件前標註一個 @Valid,Spring MVC 框架在將請求引數繫結到該入參物件後,就會呼叫校驗框架根據註解宣告的校驗規則實施校驗
  • Spring MVC 是通過對處理方法簽名的規約來儲存校驗結果的:前一個表單/命令物件的校驗結果儲存到隨後的入參中,這個儲存校驗結果的入參必須是 BindingResult 或 Errors 型別,這兩個類都位於 org.springframework.validation 包中

  • 需校驗的 Bean 物件和其繫結結果物件或錯誤物件時成對出現的,它們 之間不允許宣告其他的入參
  • Errors 介面提供了獲取錯誤資訊的方法,如 getErrorCount() 或 getFieldErrors(String field)
  • BindingResult 擴充套件了 Errors 介面

在目標方法中獲取校驗結果

  • 在表單/命令物件類的屬性中標註校驗註解,在處理方法對 應的入參前新增 @Valid,Spring MVC 就會實施校驗並將校驗結果儲存在被校驗入參物件之後的 BindingResult 或 Errors 入參中。
  • 常用方法:
    • FieldError getFieldError(String field)
    • List getFieldErrors()
    • Object getFieldValue(String field)
    • Int getErrorCount()

在頁面上顯示錯誤

  • Spring MVC 除了會將表單/命令物件的校驗結果儲存到對應的 BindingResult 或 Errors 物件中外,還會將所有校驗 結果儲存到 “隱含模型”
  • 即使處理方法的簽名中沒有對應於表單/命令物件的結果入參,校驗結果也會儲存在 “隱含物件” 中。
  • 隱含模型中的所有資料最終將通過 HttpServletRequest 的屬性列表暴露給 JSP 檢視物件,因此在 JSP 中可以獲取錯誤資訊
  • 在 JSP 頁面上可通過
    顯示錯誤訊息

示例

index.jsp

<form:form action="hello/handle19.action" modelAttribute="user">
  <form:errors path="*"></form:errors>
  
  name:<input type="text" name="userName"/>
  <form:errors path="userName"></form:errors>

  email:<input type="text" name="email"/>
  <form:errors path="email"></form:errors>

  <input type="submit" value="Submit"/>
</form:form>

User.java

public class User {
    @DateTimeFormat(pattern="yyyy/MM/dd")
    @Past
    private Date birthday;

    @NumberFormat(pattern="#,###.##")
    @DecimalMax("9999")
    @DecimalMin("1000")
    private long salary;

    @Pattern(regexp="\\w{4,30}")
    private String userName;

    @Email
    private String email;
}

目標方法

@RequestMapping("/handle19")
public String handle19(@Valid @ModelAttribute("user") User user,BindingResult bindingResult) {
    if(bindingResult.hasErrors) {
        return "forward:/index.jsp"
    } else {
        System.out.println("驗證通過。。。");
    }
    user.setId(1000);
    System.out.println(user);
    return "success"
}

提示訊息的國際化

  • 每個屬性在資料繫結和資料校驗發生錯誤時,都會生成一個對應的 FieldError 物件。
  • 當一個屬性校驗失敗後,校驗框架會為該屬性生成 4 個訊息程式碼,這些程式碼以校驗註解類名為字首,結合 modleAttribute、屬性名及屬性型別名生成多個對應的訊息程式碼:例如 User 類中的 password 屬性標準了一個 @Pattern 註解,當該屬性值不滿足 @Pattern 所定義的規則時, 就會產生以下 4 個錯誤程式碼:
    • Pattern.user.password
    • Pattern.password
    • Pattern.java.lang.String
    • Pattern
  • 當使用 Spring MVC 標籤顯示錯誤訊息時, Spring MVC 會檢視 WEB 上下文是否裝配了對應的國際化訊息,如果沒有,則顯示預設 的錯誤訊息,否則使用國際化訊息。

  • 若資料型別轉換或資料格式轉換時發生錯誤,或該有的引數不存在,或呼叫處理方法時發生錯誤,都會在隱含模型中建立錯誤訊息。其錯誤程式碼字首說明如下:
    • required:必要的引數不存在。如 @RequiredParam(“param1”) 標註了一個入參,但是該引數不存在
    • typeMismatch:在資料繫結時,發生資料型別不匹配的問題
    • methodInvocation:Spring MVC 在呼叫處理方法時發生了錯誤
  • 註冊國際化資原始檔
    xml <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="i18n"></property> </bean>