① Spring MVC 主架構将 ServletRequest 對象及目标方法的入參執行個體傳遞給 WebDataBinderFactory 執行個體,以建立 DataBinder 執行個體對象
② DataBinder 調用裝配在 Spring MVC 上下文中的ConversionService 元件進行資料類型轉換、資料格式化工作。将 Servlet 中的請求資訊填充到入參對象中
③ 調用 Validator 元件對已經綁定了請求消息的入參對象進行資料合法性校驗,并最終生成資料綁定結果BindingData 對象
④ Spring MVC 抽取 BindingResult 中的入參對象和校驗錯誤對象,将它們賦給處理方法的響應入參
1.1. 運作機制
Spring MVC 通過反射機制對目标處理方法進行解析,将請求消息綁定到處理方法的入參中。資料綁定的核心部件是DataBinder,運作機制如下:
1.2. 源碼分析
1.3. ConversionService 類型轉換器
① ConversionService 是 Spring 類型轉換體系的核心接口。
②可以利用 ConversionServiceFactoryBean 在Spring的IOC容器中定義一個 ConversionService. Spring 将自動識别出IOC 容器中的 ConversionService,并在 Bean 屬性配置及Spring MVC 處理方法入參綁定等場合使用它進行資料的轉換
③ 可通過 ConversionServiceFactoryBean 的 converters 屬性注冊自定義的類型轉換器
1.4. 自定義類型轉換器
Spring 定義了 3 種類型的轉換器接口,實作任意一個轉換器接口都可以作為自定義轉換器注冊到
ConversionServiceFactroyBean 中:
– Converter<S,T>:将 S 類型對象轉為 T 類型對象
– ConverterFactory:将相同系列多個 “同質” Converter 封裝在一起。如果希望将一種類型的對象轉換為另一種類型及其子類的對象(例如将 String 轉換為 Number 及 Number 子類(Integer、Long、Double 等)對象)可使用該轉換器工廠類
– GenericConverter:會根據源類對象及目标類對象所在的宿主類中的上下文資訊進行類型轉換
現在自己實作一個類型轉換器将字元串轉換成員工
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:component-scan base-package="com.ibigsea.springmvc"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven conversion-service="converterService"/>
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.ibigsea.springmvc.conversion.DepartmentConversion"></bean>
</list>
</property>
</bean>
</beans>
DepartmentConversion.java
package com.ibigsea.springmvc.conversion;
import org.springframework.core.convert.converter.Converter;
import com.ibigsea.springmvc.model.Department;
public class DepartmentConversion implements Converter<String, Department>{
/**
* 傳入的資料格式
* xx-xx
*/
@Override
public Department convert(String data) {
if (data == null) {
return null;
}
String [] args = data.split("-");
if (args!=null && args.length != 2) {
return null;
}
return new Department(Integer.parseInt(args[0]), args[1]);
}
}
ConversionController.java
package com.ibigsea.springmvc.rest;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ibigsea.springmvc.model.Employee;
@Controller
public class ConversionController {
@RequestMapping("/conversion")
public String toConversionData(){
return "conversionData";
}
@RequestMapping("/addemp")
public String addEmp(Employee emp,Map<String, Object> map){
map.put("emp", emp);
return "conversionData";
}
}
conversionData.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="addemp" method="post">
<input type="hidden" name="id" ><br/><br/>
name : <input type="text" name="name" ><br/><br/>
email : <input type="text" name="email" ><br/><br/>
sex :<input type="radio" name="sex" value="0" >男
<input type="radio" name="sex" value="1" >女<br/><br/>
department : <input type="text" name="department" ><br><br>
<input type="submit" value="送出">
</form>
${emp}
</body>
</html>
1.5. 資料格式化
對屬性對象的輸入/輸出進行格式化,從其本質上講依然屬于 “類型轉換” 的範疇。
① Spring 在格式化子產品中定義了一個實作ConversionService 接口的
FormattingConversionService 實作類,該實作類擴充了 GenericConversionService,是以它既具有類型轉換的功能,又具有格式化的功能
② FormattingConversionService 擁有一個 工廠類,後者用于在 Spring 上下文中構造前者
FormattingConversionServiceFactroyBean 内部已經注冊了 :
– NumberFormatAnnotationFormatterFactroy:支援對數字類型的屬性使用 @NumberFormat 注解
–JodaDateTimeFormatAnnotationFormatterFactroy:支援對日期類型的屬性使用 @DateTimeFormat 注解
• 裝配了 FormattingConversionServiceFactroyBean 後,就可以在 Spring MVC 入參綁定及模型資料輸出時使用注解驅動了。<mvc:annotation-driven/> 預設建立的ConversionService 執行個體即為FormattingConversionServiceFactroyBean
如果自定義了類型轉換器 隻用将Class配置為FormattingConversionServiceFactroyBean即可
spring-mvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:component-scan base-package="com.ibigsea.springmvc"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven conversion-service="converterService"/>
<bean id="converterService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.ibigsea.springmvc.conversion.DepartmentConversion"></bean>
</list>
</property>
</bean>
</beans>
1.5.1. @DateTimeFormat
@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:完整日期/時間格式、-:忽略日期或時間格式
1.5.2. @NumberFormat
@NumberFormat 可對類似數字類型的屬性進行标
注,它擁有兩個互斥的屬性:
– style:類型為 NumberFormat.Style。用于指定樣式類型,包括三種:Style.NUMBER(正常數字類型)、Style.CURRENCY(貨币類型)、 Style.PERCENT(百分數類型)
– pattern:類型為 String,自定義樣式,如patter="#,###";
1.5.3. FormattingConversionService轉換
1.6. JSR 303 校驗
JSR 303 是 Java 為 Bean 資料合法性校驗提供的标準架構,它已經包含在 JavaEE 6.0 中 .
• JSR 303 通過在 Bean 屬性上标注類似于 @NotNull、@Max等标準的注解指定校驗規則,并通過标準的驗證接口對 Bean進行驗證
1.6.1. Hibernate Validator 擴充注解
在pom.xml 中加入
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.2.Final</version>
</dependency>
Hibernate Validator 是 JSR 303 的一個參考實作,除支援所有标準的校驗注解外,它還支援以下的擴充注解
1.6.2. SpringMVC 資料校驗
Spring 4 擁有自己獨立的資料校驗架構,同時支援 JSR303 标準的校驗架構。
① 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 包中