最近在学习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
注册用户包含三项信息: 用户名,密码,邮箱。
<%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form"%> <!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>User Register Page</title> <style type="text/css"> .error{ color: red; } </style> </head> <body> <%-- 这里指定页面绑定的对象 modelAttribute. 之前很困惑, 为什么<form>上最重要的 <form action="someAction.do">属性没了呢? 后来发现,其实在controller中的方法以及指定了地址到method的对应关系, <form>里的action属性就可以退休了。 --%> <sf:form method="post" modelAttribute="user"> <p>用户注册页面:</p> <table width="60%" align="center"> <colgroup> <col width="10%" align="right" /> <col /> </colgroup> <tr> <th>用户名:</th> <td> <sf:input path="userName" /> <small>length of userName is not more than 20.</small><br /> <%-- 显示关于userName属性相关的错误信息。 --%> <sf:errors path="userName" cssClass="error"/> </td> </tr> <tr> <th>密码:</th> <td> <sf:password path="password" /> <small>length of password is not less than 6.</small><br /> <sf:errors path="password" cssClass="error" /> </td> </tr> <tr> <th>邮箱:</th> <td> <sf:input path="email"/> <small>format should confirm to general standard.</small><br /> <sf:errors path="email" cssClass="error" /> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="注册" /> </td> </tr> </table> </sf:form> </body> </html>这里没有使用前段的JavaScript校验,目的是为了了解和熟悉使用后台的校验机制。
public class User { private String userName; private String password; private String email; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String toString(){ StringBuilder sb = new StringBuilder(); sb.append(getClass()).append("[") .append("userName=").append(userName).append(", ") .append("password=").append(password).append(", ") .append("email=").append(email).append("]"); return sb.toString(); } }
package org.springframework.validation; public interface Validator { boolean supports(Class<?> clazz); void validate(Object target, Errors errors); }
要使用它进行校验必须实现该接口。实现Validator接口的代码如下:
package org.study.validation.validator; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; import org.study.domain.User; public class UserValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return clazz.equals(User.class); } @Override public void validate(Object target, Errors errors) { ValidationUtils.rejectIfEmpty(errors, "userName", "user.userName.required", "用户名不能为空"); ValidationUtils.rejectIfEmpty(errors, "password", "user.password.required", "密码不能为空"); ValidationUtils.rejectIfEmpty(errors, "email", "user.email.required", "邮箱不能为空"); User user = (User)target; int length = user.getUserName().length(); if(length>20){ errors.rejectValue("userName", "user.userName.too_long", "用户名不能超过{20}个字符"); } length = user.getPassword().length(); if(length <6){ errors.rejectValue("password", "user.password.too_short", "密码太短,不能少于{6}个字符"); }else if(length>20){ errors.rejectValue("password", "user.password.too_long", "密码太长,不能长于{20}个字符"); } int index = user.getEmail().indexOf("@"); if(index == -1){ errors.rejectValue("email", "user.email.invalid_email", "邮箱格式错误"); } } }
@InitBinder protected void initBinder(WebDataBinder binder){ binder.setValidator(new UserValidator()); }
/** * 处理提交的用户注册信息。 * @param model * @return */ @RequestMapping (method = RequestMethod.POST) public String doRegister(@Validated User user, BindingResult result){ if(logger.isDebugEnabled()){ logger.debug("process url[/user], method[post] in "+getClass()); } //校验没有通过 if(result.hasErrors()){ return "user"; } if(user != null){ userService.saveUser(user); } return "user"; }
环境准备:
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配置文件中,使用标签:
<mvc:annotation-driven />配置对JSR303的支持,包括制动查找Hibernate-Validator的实现等工作。
表 1. Bean Validation 规范内嵌的约束注解定义 约束注解名称 约束注解说明 @Null 验证对象是否为空 @NotNull 验证对象是否为非空 @AssertTrue 验证 Boolean 对象是否为 true @AssertFalse 验证 Boolean 对象是否为 false @Min 验证 Number 和 String 对象是否大等于指定的值 @Max 验证 Number 和 String 对象是否小等于指定的值 @DecimalMin 验证 Number 和 String 对象是否大等于指定的值,小数存在精度 @DecimalMax 验证 Number 和 String 对象是否小等于指定的值,小数存在精度 @Size 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 @Digits 验证 Number 和 String 的构成是否合法 @Past 验证 Date 和 Calendar 对象是否在当前时间之前 @Future 验证 Date 和 Calendar 对象是否在当前时间之后 @Pattern 验证 String 对象是否符合正则表达式的规则
package org.study.domain; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import org.study.validation.annotation.NotEmpty; public class User { @Size (min=3, max=20, message="用户名长度只能在3-20之间") private String userName; @Size (min=6, max=20, message="密码长度只能在6-20之间") private String password; @Pattern (regexp="[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}", message="邮件格式错误") private String email; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String toString(){ StringBuilder sb = new StringBuilder(); sb.append(getClass()).append("[") .append("userName=").append(userName).append(", ") .append("password=").append(password).append(", ") .append("email=").append(email).append("]"); return sb.toString(); } }
/** * 处理提交的用户注册信息。 * @param model * @return */ @RequestMapping (method = RequestMethod.POST) public String doRegister(@Valid User user, BindingResult result){ if(logger.isDebugEnabled()){ logger.debug("process url[/user], method[post] in "+getClass()); } //校验没有通过 if(result.hasErrors()){ return "user"; } if(user != null){ userService.saveUser(user); } return "user"; }
最后对参考文献中作者们的贡献表示感谢。
参考文献: