JSR 303(Bean Validation )
說明:
在任何時候,當你要處理一個應用程序的業務邏輯,數據校驗是你必須要考慮和面對的事情。應用程序必須通過某種手段來確保輸入進來的數據從語義上來講是正確的。
在通常的情況下,應用程序是分層的,不同的層由不同的開發人員來完成。很多時候同樣的數據驗證邏輯會出現在不同的層,這樣就會導致代碼冗余和一些管理的問題,比如說語義的一致性等。為了避免這樣的情況發生,最好是將驗證邏輯與相應的域模型進行綁定。
Bean Validation 為 JavaBean 驗證定義了相應的元數據模型和 API。默認的元數據是 Java Annotations,也可以通過使用 XML 可以對原有的元數據信息進行覆蓋和擴展。在應用程序中,通過使用 Bean Validation 或是你自己定義的 constraint(約束/限制),例如 @NotNull, @Max, @ZipCode, 就可以確保數據模型(JavaBean)的正確性。constraint 可以附加到字段,getter 方法,類或者接口上面。對於一些特定的需求,用戶可以很容易的開發定制化的 constraint。Bean Validation 是一個運行時的數據驗證框架,在驗證之后驗證的錯誤信息會被馬上返回。
說明:
下載 JSR 303 – Bean Validation 規范 http://jcp.org/en/jsr/detail?id=303
Hibernate Validator 是 Bean Validation 的參考實現 . Hibernate Validator 提供了 JSR 303 規范中所有內置 constraint 的實現,除此之外還有一些附加的 constraint。如果想了解更多有關 Hibernate Validator 的信息,請查看 http://www.hibernate.org/subprojects/validator.html
Bean Validation 中的 constraint
表 1. Bean Validation 中內置的 constraint
Constraint | 詳細信息 |
---|---|
@Null | 被注釋的元素必須為 null |
@NotNull | 被注釋的元素必須不為 null |
@AssertTrue | 被注釋的元素必須為 true |
@AssertFalse | 被注釋的元素必須為 false |
@Min(value) | 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值 |
@Max(value) | 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值 |
@DecimalMin(value) | 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值 |
@DecimalMax(value) | 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值 |
@Size(max, min) | 被注釋的元素的大小必須在指定的范圍內 |
@Digits (integer, fraction) | 被注釋的元素必須是一個數字,其值必須在可接受的范圍內 |
@Past | 被注釋的元素必須是一個過去的日期 |
@Future | 被注釋的元素必須是一個將來的日期 |
@Pattern(value) | 被注釋的元素必須符合指定的正則表達式 |
表 2. Hibernate Validator 附加的 constraint
Constraint | 詳細信息 |
---|---|
被注釋的元素必須是電子郵箱地址 | |
@Length | 被注釋的字符串的大小必須在指定的范圍內 |
@NotEmpty | 被注釋的字符串的必須非空 |
@Range | 被注釋的元素必須在合適的范圍內 |
在IDEA中使用JSR 303
加入Jar包
說明:切記要同時拷貝入在Tomcat的lib目錄中。
在要驗證的的JavaBean中加入約束條件
package domain; import javax.validation.constraints.Past; import javax.validation.constraints.Size; import java.io.Serializable; import java.util.Date; /** * Created by zy on 17-2-28. */ public class Product implements Serializable { //實現了這個接口,可以安全的將數據保存到HttpSession中 private static final long serialVersionUID= 748392348L; /** * 序列化運行時使用一個稱為 serialVersionUID 的版本號與每個可序列化類相關聯,該序列號在反序列化過程中用於驗證序列化對象的發送者和接收者是否為該對象加載了與序列化兼容的類。如果接收者加載的該對象的類的 serialVersionUID 與對應的發送者的類的版本號不同,則反序列化將會導致 InvalidClassException。可序列化類可以通過聲明名為 "serialVersionUID" 的字段(該字段必須是靜態 (static)、最終 (final) 的 long 型字段)顯式聲明其自己的 serialVersionUID: */ private long id; //被注釋的元素的大小必須在指定的范圍內 @Size(min = 1,max = 10,message="不能超過十個字符") private String name; private String description; private String price; //被注釋的元素必須是一個過去的日期 @Past private Date productionDate; public Date getProductionDate() { return productionDate; } public void setProductionDate(Date productionDate) { this.productionDate = productionDate; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public String toString() { return "Product{" + "id=" + id + ", name='" + name + '\'' + ", description='" + description + '\'' + ", price='" + price + '\'' + ", productionDate=" + productionDate + '}'; } }
在要進行驗證的控制器參數前加上@Valid
@RequestMapping(value = "/product_save",method = RequestMethod.POST) public String saveProduct(@Valid @ModelAttribute Product product, BindingResult bindingResult,Model model) { logger.info("saveProduct called"); if(bindingResult.hasErrors()) { List<ObjectError>objectErrors=bindingResult.getAllErrors(); Iterator<ObjectError> it =objectErrors.iterator(); while (it.hasNext()) { System.out.println(it.next().toString()); } //FieldError fieldError = bindingResult.getFieldError(); //logger.info("code:"+fieldError.getCode()+",object:"+fieldError.getObjectName()+",field:"+fieldError.getField()); product.setProductionDate(null); model.addAttribute("product",product); return "ProductForm"; } model.addAttribute("product",product); return "ProductForm"; }
其他:調用轉換器,來規范日期時間
1.編譯轉換器
package converter; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyConverter implements Converter<String,Date> { //<源類型,目標類型> private String dataPattern; public MyConverter(String dataPattern) { this.dataPattern=dataPattern; System.out.println("DataPattern is"+dataPattern); } public Date convert(String s) { try { SimpleDateFormat simpleDateFormat= new SimpleDateFormat(dataPattern); simpleDateFormat.setLenient(false); //設置日期/時間的解析是否不嚴格,為false表示嚴格 return simpleDateFormat.parse(s); } catch (ParseException e) { e.printStackTrace(); } return null; } }
2.在SpringMVC的xml文件中配置轉換器
<!--【注冊轉換器】--> <mvc:annotation-driven conversion-service="conversionService"/> <!--【配置轉換器】--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="converter.MyConverter"> <constructor-arg type="java.lang.String" value="MM-dd-yyyy"/> </bean> </list> </property> </bean>
測試應用
輸入錯誤表單信息:
控制台顯示錯誤日志:
Field error in object 'product' on field 'name': rejected value [我就是要超過十個字符啊]; codes [Size.product.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [product.name,name]; arguments []; default message [name],10,1]; default message [不能超過十個字符]
Field error in object 'product' on field 'productionDate': rejected value [Sat Dec 23 00:00:00 CST 2017]; codes [Past.product.productionDate,Past.productionDate,Past.java.util.Date,Past]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [product.productionDate,productionDate]; arguments []; default message [productionDate]]; default message [需要是一個過去的時間]