天天看點

Spring MVC 資料轉換 & 資料格式化 & 資料校驗資料綁定流程

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

資料綁定流程

  1. Spring MVC 主架構将 ServletRequest 對象及目标方法的入參執行個體傳遞給 WebDataBinderFactory 執行個體,以建立DataBinder 執行個體對象
  2. DataBinder 調用裝配在 Spring MVC 上下文中的ConversionService 元件進行資料類型轉換、資料格式化工作。将 Servlet中的請求資訊填充到入參對象中
  3. 調用 Validator 元件對已經綁定了請求消息的入參對象 進行資料合法性校驗,并最終生成資料綁定結果BindingData 對象
  4. Spring MVC 抽取 BindingResult 中的入參對象和校驗錯誤對象,将它們賦給處理方法的響應入參

Spring MVC 通過反射機制對目标處理方法進行解析,将請求消息綁定到處理方法的入參中。資料綁定的核心部件是DataBinder,運作機制如下:

Spring MVC 資料轉換 & 資料格式化 & 資料校驗資料綁定流程
Spring MVC 資料轉換 & 資料格式化 & 資料校驗資料綁定流程
Spring MVC 資料轉換 & 資料格式化 & 資料校驗資料綁定流程

1 資料轉換

Spring MVC 上下文中内建了很多轉換器,可完成大多數 Java 類型的轉換工作。

ConversionService converters =

Spring MVC 資料轉換 & 資料格式化 & 資料校驗資料綁定流程

自定義類型轉換器

  • ConversionService 是 Spring 類型轉換體系的核心接口。
  • 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定義一個 ConversionService. Spring 将自動識别出IOC 容器中的 ConversionService,并在 Bean 屬性配置及Spring MVC 處理方法入參綁定等場合使用它進行資料的轉換
  • 可通過 ConversionServiceFactoryBean 的 converters 屬性注冊自定義的類型轉換器
<!-- 配置 ConversionService -->
	<bean id="conversionService"
		class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
		<property name="converters">
			<set>
				<ref bean="employeeConverter"/>
			</set>
		</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-service="conversionService"></mvc:annotation-driven>	
	<!-- 配置 ConversionService -->
	<bean id="conversionService"
		class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
		<property name="converters">
			<set>
				<ref bean="employeeConverter"/>
			</set>
		</property>	
	</bean>
           
@RequestMapping("/testConversionServiceConverer")
	public String testConverter(@RequestParam("employee") Employee employee){
		System.out.println("save: " + employee);
		employeeDao.save(employee);
		return "redirect:/emps";
	}
           

關于 mvc:annotation-driven

⚫<mvc:annotation-driven /> 會自動注冊RequestMappingHandlerMapping、RequestMappingHandlerAdapter 與ExceptionHandlerExceptionResolver 三個bean。

還将提供以下支援:

  • 支援使用 ConversionService 執行個體對表單參數進行類型轉換
  • 支援使用 @NumberFormat annotation、@DateTimeFormat注解完成資料類型的格式化
  • 支援使用 @Valid 注解對 JavaBean 執行個體進行 JSR 303 驗證
  • 支援使用 @RequestBody 和 @ResponseBody 注解
Spring MVC 資料轉換 &amp; 資料格式化 &amp; 資料校驗資料綁定流程

@InitBinder

  • 由 @InitBinder 辨別的方法,可以對 WebDataBinder 對 象進行初始化。WebDataBinder 是 DataBinder 的子類,用于完成由表單字段到 JavaBean 屬性的綁定
  • @InitBinder方法不能有傳回值,它必須聲明為void。
  • @InitBinder方法的參數通常是是 WebDataBinder
@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.setDisallowedFields("lastname");
	}
           

2 資料格式化

  • 對屬性對象的輸入/輸出進行格式化,從其本質上講依然屬于 “類型轉換” 的範疇。
  • 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="#,###";
public class Employee {
	@Past
	@DateTimeFormat(pattern="yyyy-MM-dd")
	private Date birth;
	
	@NumberFormat(pattern="#,###,###.#")
	private Float salary;
}



@RequestMapping(value = "/emp", method = RequestMethod.POST)
	public String save(Employee employee) {
		employeeDao.save(employee);
		System.out.println("save: " + employee);
		return "redirect:/emps";
	}
           

3 資料校驗 JSR 303

⚫JSR 303 是 Java 為 Bean 資料合法性校驗提供的标準架構,它已經包含在 JavaEE 6.0 中 .

⚫JSR 303 通過在 Bean 屬性上标注類似于 @NotNull、@Max 等标準的注解指定校驗規則,并通過标準的驗證接口對 Bean 進行驗證

Spring MVC 資料轉換 &amp; 資料格式化 &amp; 資料校驗資料綁定流程

Hibernate Validator 擴充注解

Hibernate Validator 是 JSR 303 的一個參考實作,除支援所有标準的校驗注解外,它還支援以下的擴充注解

Spring MVC 資料轉換 &amp; 資料格式化 &amp; 資料校驗資料綁定流程

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 接口
    Spring MVC 資料轉換 &amp; 資料格式化 &amp; 資料校驗資料綁定流程

在目标方法中擷取校驗結果

在表單/指令對象類的屬性中标注校驗注解,在處理方法對 應的入參前添加 @Valid,Spring MVC 就會實施校驗并将校驗結果儲存在被校驗入參對象之後的 BindingResult 或Errors 入參中。

常用方法:

  • FieldError getFieldError(String field)
  • List<FieldError> getFieldErrors()
  • Object getFieldValue(String field)
  • Int getErrorCount()

在頁面上顯示錯誤

  • Spring MVC 除了會将表單/指令對象的校驗結果儲存到對 應的 BindingResult 或 Errors 對象中外,還會将所有校驗 結果存到 “隐含模型”
  • 即使處理方法的簽名中沒有對應于表單/指令對象的結果入參,校驗結果也會儲存在 “隐含對象” 中。
  • 隐含模型中的所有資料最終将通過 HttpServletRequest 的屬性清單暴露給 JSP 視圖對象,是以在 JSP 中可以擷取錯誤資訊
  • 在 JSP 頁面上可通過 <form:errors path=“userName”> 顯示錯誤消息
Birth: <form:input path="birth" />
		<form:errors path="birth"></form:errors>
		<br>
		Salary: <form:input path="salary" />
		<br>
           
public class Employee {

	private Integer id;
	@NotEmpty
	private String lastName;

	@Email
	private String email;
	//1 male, 0 female
	private Integer gender;
	
	private Department department;
	
	@Past
	@DateTimeFormat(pattern="yyyy-MM-dd")
	private Date birth;
	
	@NumberFormat(pattern="#,###,###.#")
	private Float salary;
}


@RequestMapping(value = "/emp", method = RequestMethod.POST)
	public String save(@Valid Employee employee, Errors result, Map<String, Object> map) {
		System.out.println("save: " + employee);

		if (result.getErrorCount() > 0) {
			System.out.println("出錯了!");

			for (FieldError error : result.getFieldErrors()) {
				System.out.println(error.getField() + ":" + error.getDefaultMessage());
			}

			// 若驗證出錯, 則轉向定制的頁面
			map.put("departments", departmentDao.getDepartments());
			return "input";
		}

		employeeDao.save(employee);
		return "redirect:/emps";
	}
           

提示消息的國際化

⚫每個屬性在資料綁定和資料校驗發生錯誤時,都會生成一個對應的 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在調用處理方法時發生了錯誤

⚫注冊國際化資源檔案

<bean id="messageSource"
		class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basename" value="i18n"></property>
	</bean>
           

繼續閱讀