SpringMVC:学习笔记(7)——验证器(JSR303)
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 [需要是一个过去的时间]