• (转)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

  • 相关阅读:
    装饰者模式【结构模式】
    代理模式【结构模式】
    原型模式【构建模式】
    建造者模式【构建模式】
    抽象工厂模式【构建模式】
    工厂模式【构建模式】
    单例模式【构建模式】
    设计原则
    Collector 源码分析
    Maven 包命令
  • 原文地址:https://www.cnblogs.com/yamadie/p/2973033.html
Copyright © 2020-2023  润新知