转自:http://blog.csdn.net/lu930124/article/details/52587135

JSR-303是一个数据验证的规范,这里我不会讲这个规范是怎么回事,只会讲一下JSR-303在SpringMVC中的应用。JSR-303只是一个规范,而Spring也没有对这一规范进行实现,那么当我们在SpringMVC中需要使用到JSR-303的时候就需要我们提供一个对JSR-303规范的实现,Hibernate Validator是实现了这一规范的,这里我将以它作为JSR-303的实现来讲解SpringMVC对JSR-303的支持。

JSR-303的校验是基于注解的,它内部已经定义好了一系列的限制注解,我们只需要把这些注解标记在需要验证的实体类的属性上或是其对应的get方法上。来看以下一个需要验证的实体类User的代码:

  1. import javax.validation.constraints.Min;
  2. import javax.validation.constraints.NotNull;
  3. import org.hibernate.validator.constraints.NotBlank;
  4. public class User {
  5. private String username;
  6. private String password;
  7. private int age;
  8. @NotBlank(message="用户名不能为空")
  9. public String getUsername() {
  10. return username;
  11. }
  12. public void setUsername(String username) {
  13. this.username = username;
  14. }
  15. @NotNull(message="密码不能为null")
  16. public String getPassword() {
  17. return password;
  18. }
  19. public void setPassword(String password) {
  20. this.password = password;
  21. }
  22. @Min(value=10, message="年龄的最小值为10")
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. }

我们可以看到我们在username、password和age对应的get方法上都加上了一个注解,这些注解就是JSR-303里面定义的限制,其中@NotBlank是Hibernate Validator的扩展。不难发现,使用JSR-303来进行校验比使用Spring提供的Validator接口要简单的多。我们知道注解只是起到一个标记性的作用,它是不会直接影响到代码的运行的,它需要被某些类识别到才能起到限制作用。使用SpringMVC的时候我们只需要把JSR-303的实现者对应的jar包放到classpath中,然后在SpringMVC的配置文件中引入MVC Namespace,并加上<mvn:annotation-driven/>就可以非常方便的使用JSR-303来进行实体对象的验证。加上了<mvn:annotation-driven/>之后Spring会自动检测classpath下的JSR-303提供者并自动启用对JSR-303的支持,把对应的校验错误信息放到Spring的Errors对象中。这时候SpringMVC的配置文件如下所示:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:mvc="http://www.springframework.org/schema/mvc"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context-3.0.xsd
  9. http://www.springframework.org/schema/mvc
  10. http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
  11. <mvc:annotation-driven/>
  12. </beans>

接着我们来定义一个使用User对象作为参数接收者的Controller,其代码如下所示:

  1. import javax.validation.Valid;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.validation.BindingResult;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. @Controller
  6. public class UserController {
  7. @RequestMapping("login")
  8. public String login(@Valid User user, BindingResult result) {
  9. if (result.hasErrors())
  10. return "user/login";
  11. return "redirect:/";
  12. }
  13. }

这样当我们不带任何参数请求login.do的时候就不能通过实体对象User的属性数据有效性限制,然后会把对应的错误信息放置在当前的Errors对象中。
JSR-303原生支持的限制有如下几种:
限制

除了JSR-303原生支持的限制类型之外我们还可以定义自己的限制类型。定义自己的限制类型首先我们得定义一个该种限制类型的注解,而且该注解需要使用@Constraint标注。现在假设我们需要定义一个表示金额的限制类型,那么我们可以这样定义:

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. import javax.validation.Constraint;
  6. import javax.validation.Payload;
  7. import com.xxx.xxx.constraint.impl.MoneyValidator;
  8. @Target({ElementType.FIELD, ElementType.METHOD})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Constraint(validatedBy=MoneyValidator.class)
  11. public @interface Money {
  12. String message() default"不是金额形式";
  13. Class<?>[] groups() default {};
  14. Class<? extends Payload>[] payload() default {};
  15. }

我们可以看到在上面代码中我们定义了一个Money注解,而且该注解上标注了@Constraint注解,使用@Constraint注解标注表明我们定义了一个用于限制的注解。@Constraint注解的validatedBy属性用于指定我们定义的当前限制类型需要被哪个ConstraintValidator进行校验。在上面代码中我们指定了Money限制类型的校验类是MoneyValidator。另外需要注意的是我们在定义自己的限制类型的注解时有三个属性是必须定义的,如上面代码所示的message、groups和payload属性。
       在定义了限制类型Money之后,接下来就是定义我们的限制类型校验类MoneyValidator了。限制类型校验类必须实现接口javax.validation.ConstraintValidator,并实现它的initialize和isValid方法。我们先来看一下MoneyValidator的代码示例:

  1. import java.util.regex.Pattern;
  2. import javax.validation.ConstraintValidator;
  3. import javax.validation.ConstraintValidatorContext;
  4. import com.xxx.xxx.constraint.Money;
  5. public class MoneyValidator implements ConstraintValidator<Money, Double> {
  6. private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
  7. private Pattern moneyPattern = Pattern.compile(moneyReg);
  8. public void initialize(Money money) {
  9. // TODO Auto-generated method stub
  10. }
  11. public boolean isValid(Double value, ConstraintValidatorContext arg1) {
  12. // TODO Auto-generated method stub
  13. if (value == null)
  14. return true;
  15. return moneyPattern.matcher(value.toString()).matches();
  16. }
  17. }

从上面代码中我们可以看到ConstraintValidator是使用了泛型的。它一共需要指定两种类型,第一个类型是对应的initialize方法的参数类型,第二个类型是对应的isValid方法的第一个参数类型。从上面的两个方法我们可以看出isValid方法是用于进行校验的,有时候我们在校验的过程中是需要取当前的限制类型的属性来进行校验的,比如我们在对@Min限制类型进行校验的时候我们是需要通过其value属性获取到当前校验类型定义的最小值的,我们可以看到isValid方法无法获取到当前的限制类型Money。这个时候initialize方法的作用就出来了。我们知道initialize方法是可以获取到当前的限制类型的,所以当我们在校验某种限制类型时需要获取当前限制类型的某种属性的时候,我们可以给当前的ConstraintValidator定义对应的属性,然后在initialize方法中给该属性赋值,接下来我们就可以在isValid方法中使用其对应的属性了。针对于这种情况我们来看一个代码示例,现在假设我要定义自己的@Min限制类型和对应的MinValidator校验器,那么我可以如下定义:

Min限制类型

  1. @Target({ElementType.FIELD, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Constraint(validatedBy=MinValidator.class)
  4. public @interface Min {
  5. int value() default 0;
  6. String message();
  7. Class<?>[] groups() default {};
  8. Class<? extends Payload>[] payload() default {};
  9. }

MinValidator校验器

  1. public class MinValidator implements ConstraintValidator<Min, Integer> {
  2. private int minValue;
  3. public void initialize(Min min) {
  4. // TODO Auto-generated method stub
  5. //把Min限制类型的属性value赋值给当前ConstraintValidator的成员变量minValue
  6. minValue = min.value();
  7. }
  8. public boolean isValid(Integer value, ConstraintValidatorContext arg1) {
  9. // TODO Auto-generated method stub
  10. //在这里我们就可以通过当前ConstraintValidator的成员变量minValue访问到当前限制类型Min的value属性了
  11. return value >= minValue;
  12. }
  13. }

继续来说一下ConstraintValidator泛型的第二个类型,我们已经知道它的第二个类型是对应的isValid的方法的第一个参数,从我给的参数名称value来看也可以知道isValid方法的第一个参数正是对应的当前需要校验的数据的值,而它的类型也正是对应的我们需要校验的数据的数据类型。这两者的数据类型必须保持一致,否则Spring会提示找不到对应数据类型的ConstraintValidator。建立了自己的限制类型及其对应的ConstraintValidator后,其用法跟标准的JSR-303限制类型是一样的。以下就是使用了上述自己定义的JSR-303限制类型——Money限制和Min限制的一个实体类:

  1. public class User {
  2. private int age;
  3. private Double salary;
  4. @Min(value=8, message="年龄不能小于8岁")
  5. public int getAge() {
  6. return age;
  7. }
  8. public void setAge(int age) {
  9. this.age = age;
  10. }
  11. @Money(message="标准的金额形式为xxx.xx")
  12. public Double getSalary() {
  13. return salary;
  14. }
  15. public void setSalary(Double salary) {
  16. this.salary = salary;
  17. }
  18. }

另外再讲一点Spring对自定义JSR-303限制类型支持的新特性,那就是Spring支持往ConstraintValidator里面注入bean对象。现在假设我们在MoneyValidator里面需要用到Spring ApplicationContext容器中的一个UserController bean对象,那么我们可以给ConstraintValidator定义一个UserController属性,并给定其set方法,在set方法上加注解@Resource或@Autowired通过set方式来注入当前的ApplicationContext中拥有的UserController bean对象。关于@Resource和@AutoWired的区别可以参考这篇博客。所以我们可以这样来定义我们的MoneyValidator:

    1. public class MoneyValidator implements ConstraintValidator<Money, Double> {
    2. private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
    3. private Pattern moneyPattern = Pattern.compile(moneyReg);
    4. private UserController controller;
    5. public void initialize(Money money) {
    6. // TODO Auto-generated method stub
    7. }
    8. public boolean isValid(Double value, ConstraintValidatorContext arg1) {
    9. // TODO Auto-generated method stub
    10. System.out.println("UserController: .............." + controller);
    11. if (value == null)
    12. returntrue;
    13. return moneyPattern.matcher(value.toString()).matches();
    14. }
    15. public UserController getController() {
    16. return controller;
    17. }
    18. @Resource
    19. public void setController(UserController controller) {
    20. this.controller = controller;
    21. }
    22. }

JSR303验证的更多相关文章

  1. SpringBoot-服务端参数验证-JSR-303验证框架

    1. springboot 默认集成了 hibernate-validator,它默认是生效的,可以直接使用. 比如: @RestController @RequestMapping("/h ...

  2. springMVC中使用 JSR-303验证器( Validation 接口 )

    在pom.xml,添加validator验证器的依赖 <dependency> <groupId>org.hibernate</groupId> <artif ...

  3. spring mvc 使用jsr-303进行表单验证的方法介绍

    源代码来源:http://howtodoinjava.com/spring/spring-mvc/spring-bean-validation-example-with-jsr-303-annotat ...

  4. SpringMVC学习系列(6) 之 数据验证

    在系列(4).(5)中我们展示了如何绑定数据,绑定完数据之后如何确保我们得到的数据的正确性?这就是我们本篇要说的内容 —> 数据验证. 这里我们采用Hibernate-validator来进行验 ...

  5. SpringMVC数据验证

    SpringMVC数据验证——第七章 注解式控制器的数据验证.类型转换及格式化——跟着开涛学SpringMVC 资源来自:http://jinnianshilongnian.iteye.com/blo ...

  6. SpringMVC 服务器端验证

    1.导入JSR303验证类库Jar包2.在MVC的配置文件中添加<mvc:annotation-driven/>的配置3.在MVC的配置文件中添加验证器的配置4.在接收表单数据的类中添加验 ...

  7. springMVC 验证器

    采用Hibernate-validator来进行验证,Hibernate-validator实现了JSR-303验证框架支持注解风格的验证.首先我们要到http://hibernate.org/val ...

  8. spring(7)--注解式控制器的数据验证、类型转换及格式化

    7.1.简介 在编写可视化界面项目时,我们通常需要对数据进行类型转换.验证及格式化. 一.在Spring3之前,我们使用如下架构进行类型转换.验证及格式化: 流程: ①:类型转换:首先调用Proper ...

  9. spring mvc 建立下拉框并进行验证demo

    原文出处:http://howtodoinjava.com/spring/spring-mvc/spring-mvc-populate-and-validate-dropdown-example/ 该 ...

随机推荐

  1. Win10系统修改电脑IP地址

    方法/步骤 1.首先,打开控制面板 2.接着,点开“网络和Internet”,再点开“网络和共享中心” 3.点击"无线网络连接IT4822",可以看到下图 4.然后点击开“属性”, ...

  2. 使用JavaScript获取select元素选中的value和text

    示例代码如下(js直接写在了html里面,没有写在一个单独的外部文件中): <!DOCTYPE html> <html> <head> <meta name= ...

  3. Spring的一种拦截器SimpleUrlHandlerMapping

    spring的一种拦截器,用于在XML文件中配置以拦截url,它是以map映射的方式进行拦截.映射是从前台urls到具体后台的beans.同时支持到bean实例和bean名称的映射,后者要求非单实例控 ...

  4. ftp安装和虚拟用户创建

    安装 1.安装 sudo apt-get install vsftpd 2 查看安装结果 安装完毕,检查vsftpd进程是否已启动,可以查看进程或者查看监听端口 ps -eaf|grep vsftpd ...

  5. thinkPHP中phpexcel的导出功能

    public function wExcel() { vendor("PHPExcel.PHPExcel"); // Create new PHPExcel object $obj ...

  6. android4.4上全屏界面实现禁止状态栏下拉

    附上我改动的方法:PhoneWindowManager.java里面的改动 --- a/frameworks/base/policy/src/com/android/internal/policy/i ...

  7. CSS伪类选择器 - nth-child(an+b):

    CSS伪类选择器 - nth-child(an+b): 第一种:简单数字序号写法:nth-child(number)直接匹配第number个元素.参数number必须为大于0的整数.li:nth-ch ...

  8. C# 导出Excel "正在中止线程" 错误

    导出Excel相信很多人都用过,但是我却遇到了一个问题 “正在中止线程” 源代码如下: public static void ExportExcel(string fileName, GridView ...

  9. 程序阅读:简单C++学生信息管理系统

    课程首页在:http://blog.csdn.net/sxhelijian/article/details/11890759,内有完整教学方案及资源链接 [程序阅读]阅读并执行以下的程序,找出当中出现 ...

  10. Spring JDBC样例

    这里介绍一下通过Spring JDBC的方式进行数据库的增删改查的操作.在进行程序的编写之前我们需要在本地MySQL数据库中创建一张User表,如下所示: create database user_d ...