(转)spring 3.1 Validator 校验机制解析

我的博客:www.while0.com

我的博客:www.shishangguan.net

最近在学习Spring3.1,基于注解的MVC, 使用过程中发现,开始的时候很不喜欢那个JSR 303 推荐的校验机制,原因很简单就是要记很多Annotation, 每个校验类型一个annotation,而且也不喜欢那个正则式的校验方式,还是喜欢独立的一个校验方法的方式,像以前的struts里的那个对form 的校验,或者spring里的校验接口那种的。但是找了老半天,发现spring3.1没有提供太多的学习资料和使用介绍。
自己在学习API和reference document的基础上,找到了这两种校验方式。在此总结下,供大家参考。

Spring 基于注释的校验机制:
1) 支持Spring框架定义的Validator接口定义的校验。
2)支持JSR303 Bean Validation定义的校验规范。
下面使用一个用户注册的例子来就二者的使用进行讲解。

平台: Spring 3.1.0

用户注册页面: 名称:user.jsp
注册用户包含三项信息: 用户名,密码,邮箱。

  1. <%@ page language="java" contentType="text/html; charset=UTF-8"%>  
  2. <%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>  
  3.   
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  8. <title>User Register Page</title>  
  9. <style type="text/css">  
  10. .error{  
  11.     color: red;   
  12. }  
  13. </style>  
  14. </head>  
  15.   
  16. <body>  
  17.     <%--  
  18.         这里指定页面绑定的对象 modelAttribute. 之前很困惑,  
  19.         为什么<form>上最重要的 <form action="someAction.do">属性没了呢?   
  20.         后来发现,其实在controller中的方法以及指定了地址到method的对应关系,  
  21.         <form>里的action属性就可以退休了。  
  22.     --%>  
  23.     <sf:form method="post" modelAttribute="user">  
  24.         <p>用户注册页面:</p>  
  25.         <table width="60%" align="center">  
  26.             <colgroup>  
  27.                 <col width="10%" align="right" />  
  28.                 <col />  
  29.             </colgroup>     
  30.             <tr>  
  31.                 <th>用户名:</th>  
  32.                 <td>  
  33.                     <sf:input path="userName" />  
  34.                     <small>length of userName is not more than 20.</small><br />  
  35.                     <%-- 显示关于userName属性相关的错误信息。 --%>  
  36.                     <sf:errors path="userName" cssClass="error"/>  
  37.                 </td>  
  38.             </tr>  
  39.             <tr>  
  40.                 <th>密码:</th>  
  41.                 <td>  
  42.                     <sf:password path="password" />  
  43.                     <small>length of password is not less than 6.</small><br />  
  44.                     <sf:errors path="password" cssClass="error" />  
  45.                 </td>  
  46.             </tr>  
  47.             <tr>  
  48.                 <th>邮箱:</th>  
  49.                 <td>  
  50.                     <sf:input path="email"/>  
  51.                     <small>format should confirm to general standard.</small><br />  
  52.                     <sf:errors path="email" cssClass="error" />  
  53.                 </td>  
  54.             </tr>           
  55.             <tr>  
  56.                 <td colspan="2" align="center">  
  57.                     <input type="submit" value="注册" />  
  58.                 </td>  
  59.             </tr>  
  60.         </table>  
  61.     </sf:form>  
  62. </body>  
  63. </html>  

这里没有使用前段的JavaScript校验,目的是为了了解和熟悉使用后台的校验机制。

与前台页面相关联的对象: User, 包含userName, password, email等属性。

  1. public class User {   
  2.     private String userName;  
  3.       
  4.     private String password;  
  5.       
  6.     private String email;  
  7.       
  8.     public String getUserName() {  
  9.         return userName;  
  10.     }  
  11.     public void setUserName(String userName) {  
  12.         this.userName = userName;  
  13.     }  
  14.     public String getPassword() {  
  15.         return password;  
  16.     }  
  17.     public void setPassword(String password) {  
  18.         this.password = password;  
  19.     }  
  20.     public String getEmail() {  
  21.         return email;  
  22.     }  
  23.     public void setEmail(String email) {  
  24.         this.email = email;  
  25.     }  
  26.       
  27.     public String toString(){  
  28.         StringBuilder sb = new StringBuilder();  
  29.           
  30.         sb.append(getClass()).append("[")  
  31.         .append("userName=").append(userName).append(", ")  
  32.         .append("password=").append(password).append(", ")  
  33.         .append("email=").append(email).append("]");  
  34.           
  35.         return sb.toString();  
  36.     }  
  37.       
  38. }  



本人比较偏好Spring框架的Validator方式校验,所以先说第一种方式。

校验方式一: Spring Validator 


1)Validator接口的实现:

Spring框架的Validator接口定义,

  1. package org.springframework.validation;  
  2.   
  3. public interface Validator {  
  4.       
  5.     boolean supports(Class<?> clazz);  
  6.       
  7.     void validate(Object target, Errors errors);  
  8.   
  9. }  

要使用它进行校验必须实现该接口。实现Validator接口的代码如下:

  1. package org.study.validation.validator;  
  2.   
  3. import org.springframework.validation.Errors;  
  4. import org.springframework.validation.ValidationUtils;  
  5. import org.springframework.validation.Validator;  
  6. import org.study.domain.User;  
  7.   
  8. public class UserValidator implements Validator {  
  9.   
  10.     @Override  
  11.     public boolean supports(Class<?> clazz) {  
  12.         return clazz.equals(User.class);  
  13.     }  
  14.   
  15.     @Override  
  16.     public void validate(Object target, Errors errors) {  
  17.         ValidationUtils.rejectIfEmpty(errors, "userName""user.userName.required""用户名不能为空");  
  18.         ValidationUtils.rejectIfEmpty(errors, "password""user.password.required""密码不能为空");  
  19.         ValidationUtils.rejectIfEmpty(errors, "email""user.email.required""邮箱不能为空");  
  20.         User user = (User)target;  
  21.         int length = user.getUserName().length();  
  22.         if(length>20){  
  23.             errors.rejectValue("userName""user.userName.too_long""用户名不能超过{20}个字符");  
  24.         }  
  25.         length = user.getPassword().length();  
  26.         if(length <6){  
  27.             errors.rejectValue("password""user.password.too_short""密码太短,不能少于{6}个字符");  
  28.         }else if(length>20){  
  29.             errors.rejectValue("password""user.password.too_long""密码太长,不能长于{20}个字符");  
  30.         }  
  31.         int index = user.getEmail().indexOf("@");  
  32.         if(index == -1){  
  33.             errors.rejectValue("email""user.email.invalid_email""邮箱格式错误");  
  34.         }  
  35.     }  
  36.   
  37. }  


2) 设置Validator,并触发校验。
在我们的Controller中需要使用父类已有的方法来为DataBinder对象指定Validator,  protected initBinder(WebDataBinder binder); 代码如下:

  1. @InitBinder  
  2.     protected void initBinder(WebDataBinder binder){  
  3.         binder.setValidator(new UserValidator());  
  4.     }  


为binder对象指定好Validator校验对象后,下面一步的就是在需要校验的时候触发validate方法,该触发步骤是通过 @Validated 注解(该注解是Spring框架定义的)实现的。

  1. /** 
  2.      * 处理提交的用户注册信息。 
  3.      * @param model 
  4.      * @return 
  5.      */  
  6.     @RequestMapping (method = RequestMethod.POST)  
  7.     public String doRegister(@Validated User user, BindingResult result){  
  8.         if(logger.isDebugEnabled()){  
  9.             logger.debug("process url[/user], method[post] in "+getClass());  
  10.         }  
  11.         //校验没有通过  
  12.         if(result.hasErrors()){  
  13.             return "user";  
  14.         }  
  15.           
  16.         if(user != null){  
  17.             userService.saveUser(user);  
  18.         }  
  19.           
  20.         return "user";  
  21.     }  


至此,从页面提交的User对象可以通过我们实现的UserValidator类来校验了,校验的结果信息存入BindingResult result对象中。在前台页面可以使用spring-form的标签<sf:errors path="*" />来显示。


校验方式二: JSR303 Bean Validation

在Spring3.1中增加的了对JSR303 Bean Validation规范的支持,不仅可以对Spring的 MVC进行校验,而且也可以对Hibernate的存储对象进行校验。是一个通用的校验框架。


环境准备:

A) 导入Hibernate-Validator  
要使用JSR303 校验框架, 需要加入框架的具体实现Hibernate-Validator, 在soureforge上下载最新的Hibernate-Validator, 当前版本为4.2.0 Final版。
在/WEB-INF/lib中导入 hibernate-validator-4.2.0.Final.jar, hibernate- validator-annotation-processor-4.2.0.Final.jar, 导入它的lib/required目录下内容slf4j-api-1.6.1.jar, validation-api-1.0.0.GA.jar;

B) 配置Spring对JSR 303 的支持。 
在你的 <servletName>-servlet.xml配置文件中,使用标签:

  1. <mvc:annotation-driven />  

配置对JSR303的支持,包括制动查找Hibernate-Validator的实现等工作。


1) 校验属性的Constraints的设定
该步骤就是对要校验的对象的属性,使用已经定义的Constraints对需要校验的属性进行约束。在JSR303中已经定义的Constraint如下:

  1. 表 1. Bean Validation 规范内嵌的约束注解定义  
  2. 约束注解名称  约束注解说明  
  3. @Null           验证对象是否为空  
  4. @NotNull    验证对象是否为非空  
  5. @AssertTrue     验证 Boolean 对象是否为 true  
  6. @AssertFalse    验证 Boolean 对象是否为 false  
  7. @Min            验证 Number 和 String 对象是否大等于指定的值  
  8. @Max            验证 Number 和 String 对象是否小等于指定的值  
  9. @DecimalMin     验证 Number 和 String 对象是否大等于指定的值,小数存在精度  
  10. @DecimalMax     验证 Number 和 String 对象是否小等于指定的值,小数存在精度  
  11. @Size           验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
  12. @Digits     验证 Number 和 String 的构成是否合法  
  13. @Past           验证 Date 和 Calendar 对象是否在当前时间之前  
  14. @Future     验证 Date 和 Calendar 对象是否在当前时间之后  
  15. @Pattern    验证 String 对象是否符合正则表达式的规则   



通过上述Constraint约束后的User对象如下:

  1. package org.study.domain;  
  2.   
  3. import javax.validation.constraints.Pattern;  
  4. import javax.validation.constraints.Size;  
  5.   
  6. import org.study.validation.annotation.NotEmpty;  
  7.   
  8. public class User {  
  9.       
  10.     @Size (min=3, max=20, message="用户名长度只能在3-20之间")  
  11.     private String userName;  
  12.       
  13.     @Size (min=6, max=20, message="密码长度只能在6-20之间")  
  14.     private String password;  
  15.       
  16.     @Pattern (regexp="[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}", message="邮件格式错误")  
  17.     private String email;  
  18.       
  19.     public String getUserName() {  
  20.         return userName;  
  21.     }  
  22.     public void setUserName(String userName) {  
  23.         this.userName = userName;  
  24.     }  
  25.     public String getPassword() {  
  26.         return password;  
  27.     }  
  28.     public void setPassword(String password) {  
  29.         this.password = password;  
  30.     }  
  31.     public String getEmail() {  
  32.         return email;  
  33.     }  
  34.     public void setEmail(String email) {  
  35.         this.email = email;  
  36.     }  
  37.       
  38.     public String toString(){  
  39.         StringBuilder sb = new StringBuilder();  
  40.           
  41.         sb.append(getClass()).append("[")  
  42.         .append("userName=").append(userName).append(", ")  
  43.         .append("password=").append(password).append(", ")  
  44.         .append("email=").append(email).append("]");  
  45.           
  46.         return sb.toString();  
  47.     }  
  48.       
  49. }  


2) Validate的触发
在需要校验的对象前增加 @Valid 注解 (该注解位于javax.validation包中)来触发校验。示例如下:

  1. /** 
  2.      * 处理提交的用户注册信息。 
  3.      * @param model 
  4.      * @return 
  5.      */  
  6.     @RequestMapping (method = RequestMethod.POST)  
  7.     public String doRegister(@Valid User user, BindingResult result){  
  8.         if(logger.isDebugEnabled()){  
  9.             logger.debug("process url[/user], method[post] in "+getClass());  
  10.         }  
  11.         //校验没有通过  
  12.         if(result.hasErrors()){  
  13.             return "user";  
  14.         }  
  15.           
  16.         if(user != null){  
  17.             userService.saveUser(user);  
  18.         }  
  19.           
  20.         return "user";  
  21.     }  


这样就可以完成针对输入数据User对象的校验了, 校验结果任然保存在BindingResult对象中。

注: 想要往页面传数据的时候,不要调用result.getModel().put(key, value);  这样做将不起作用,因为getModel()方法每次都是生成一个新的,保存的东西就会丢失。
最好在响应方法中加一个: Map<String, Object> map来保存数据。

最后对参考文献中作者们的贡献表示感谢。

参考文献:

1、 Bean Validation 技术规范特性概述  
2、JSR303 Bean Validation 
3、Spring In Action 3th, Spring MVC, chapter 7
4、spring-framework-reference.pdf  Validation, Chapter 6

原文地址:https://www.cnblogs.com/yamadie/p/2973033.html