使用JSR-303 Validation进行验证

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

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

import javax.validation.constraints.Min;  
import javax.validation.constraints.NotNull;  
import org.hibernate.validator.constraints.NotBlank;  
   
public class User {  
   
    private String username;  
     
    private String password;  
     
    private int age;  
   
    @NotBlank(message="用户名不能为空")  
    public String getUsername() {  
       return username;  
    }  
   
    public void setUsername(String username) {  
       this.username = username;  
    }  
   
    @NotNull(message="密码不能为null")  
    public String getPassword() {  
       return password;  
    }  
   
    public void setPassword(String password) {  
       this.password = password;  
    }  
   
    @Min(value=10, message="年龄的最小值为10")  
    public int getAge() {  
       return age;  
    }  
   
    public void setAge(int age) {  
       this.age = age;  
    }  
     
}  
       我们可以看到我们在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的配置文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-3.0.xsd  
     http://www.springframework.org/schema/mvc  
     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
      
    <mvc:annotation-driven/>  
</beans>

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

import javax.validation.Valid;  
import org.springframework.stereotype.Controller;  
import org.springframework.validation.BindingResult;  
import org.springframework.web.bind.annotation.RequestMapping;  
   
@Controller  
public class UserController {  
   
    @RequestMapping("login")  
    public String login(@Valid User user, BindingResult result) {  
       if (result.hasErrors())  
           return "user/login";  
       return "redirect:/";  
    }  
     
}  
       这样当我们不带任何参数请求login.do的时候就不能通过实体对象User的属性数据有效性限制,然后会把对应的错误信息放置在当前的Errors对象中。
JSR-303原生支持的限制有如下几种:
限制

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

import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
   
import javax.validation.Constraint;  
import javax.validation.Payload;  
   
import com.xxx.xxx.constraint.impl.MoneyValidator;  
   
@Target({ElementType.FIELD, ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
@Constraint(validatedBy=MoneyValidator.class)  
public @interface Money {  
     
    String message() default"不是金额形式";  
     
    Class<?>[] groups() default {};  
     
    Class<? extends Payload>[] payload() default {};  
   
} 
       我们可以看到在上面代码中我们定义了一个Money注解,而且该注解上标注了@Constraint注解,使用@Constraint注解标注表明我们定义了一个用于限制的注解。@Constraint注解的validatedBy属性用于指定我们定义的当前限制类型需要被哪个ConstraintValidator进行校验。在上面代码中我们指定了Money限制类型的校验类是MoneyValidator。另外需要注意的是我们在定义自己的限制类型的注解时有三个属性是必须定义的,如上面代码所示的message、groups和payload属性。
       在定义了限制类型Money之后,接下来就是定义我们的限制类型校验类MoneyValidator了。限制类型校验类必须实现接口javax.validation.ConstraintValidator,并实现它的initialize和isValid方法。我们先来看一下MoneyValidator的代码示例:

import java.util.regex.Pattern;  
   
import javax.validation.ConstraintValidator;  
import javax.validation.ConstraintValidatorContext;  
   
import com.xxx.xxx.constraint.Money;  
   
public class MoneyValidator implements ConstraintValidator<Money, Double> {  
   
    private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式  
    private Pattern moneyPattern = Pattern.compile(moneyReg);  
     
    public void initialize(Money money) {  
       // TODO Auto-generated method stub  
        
    }  
   
    public boolean isValid(Double value, ConstraintValidatorContext arg1) {  
       // TODO Auto-generated method stub  
       if (value == null)  
           return true;  
       return moneyPattern.matcher(value.toString()).matches();  
    }  
   
}  
       从上面代码中我们可以看到ConstraintValidator是使用了泛型的。它一共需要指定两种类型,第一个类型是对应的initialize方法的参数类型,第二个类型是对应的isValid方法的第一个参数类型。从上面的两个方法我们可以看出isValid方法是用于进行校验的,有时候我们在校验的过程中是需要取当前的限制类型的属性来进行校验的,比如我们在对@Min限制类型进行校验的时候我们是需要通过其value属性获取到当前校验类型定义的最小值的,我们可以看到isValid方法无法获取到当前的限制类型Money。这个时候initialize方法的作用就出来了。我们知道initialize方法是可以获取到当前的限制类型的,所以当我们在校验某种限制类型时需要获取当前限制类型的某种属性的时候,我们可以给当前的ConstraintValidator定义对应的属性,然后在initialize方法中给该属性赋值,接下来我们就可以在isValid方法中使用其对应的属性了。针对于这种情况我们来看一个代码示例,现在假设我要定义自己的@Min限制类型和对应的MinValidator校验器,那么我可以如下定义:

Min限制类型

@Target({ElementType.FIELD, ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
@Constraint(validatedBy=MinValidator.class)  
public @interface Min {  
   
    int value() default 0;  
     
    String message();  
     
    Class<?>[] groups() default {};  
     
    Class<? extends Payload>[] payload() default {};  
}
MinValidator校验器
public class MinValidator implements ConstraintValidator<Min, Integer> {  
   
    private int minValue;  
     
    public void initialize(Min min) {  
       // TODO Auto-generated method stub  
       //把Min限制类型的属性value赋值给当前ConstraintValidator的成员变量minValue  
       minValue = min.value();  
    }  
   
    public boolean isValid(Integer value, ConstraintValidatorContext arg1) {  
       // TODO Auto-generated method stub  
       //在这里我们就可以通过当前ConstraintValidator的成员变量minValue访问到当前限制类型Min的value属性了  
       return value >= minValue;  
    }  
   
}  
       继续来说一下ConstraintValidator泛型的第二个类型,我们已经知道它的第二个类型是对应的isValid的方法的第一个参数,从我给的参数名称value来看也可以知道isValid方法的第一个参数正是对应的当前需要校验的数据的值,而它的类型也正是对应的我们需要校验的数据的数据类型。这两者的数据类型必须保持一致,否则Spring会提示找不到对应数据类型的ConstraintValidator。建立了自己的限制类型及其对应的ConstraintValidator后,其用法跟标准的JSR-303限制类型是一样的。以下就是使用了上述自己定义的JSR-303限制类型——Money限制和Min限制的一个实体类:
public class User {  
     
    private int age;  
     
    private Double salary;  
   
    @Min(value=8, message="年龄不能小于8岁")  
    public int getAge() {  
       return age;  
    }  
   
    public void setAge(int age) {  
       this.age = age;  
    }  
   
    @Money(message="标准的金额形式为xxx.xx")  
    public Double getSalary() {  
       return salary;  
    }  
   
    public void setSalary(Double salary) {  
       this.salary = salary;  
    }  
     
}  
       另外再讲一点Spring对自定义JSR-303限制类型支持的新特性,那就是Spring支持往ConstraintValidator里面注入bean对象。现在假设我们在MoneyValidator里面需要用到Spring ApplicationContext容器中的一个UserController bean对象,那么我们可以给ConstraintValidator定义一个UserController属性,并给定其set方法,在set方法上加注解@Resource或@Autowired通过set方式来注入当前的ApplicationContext中拥有的UserController bean对象。关于@Resource和@AutoWired的区别可以参考这篇博客。所以我们可以这样来定义我们的MoneyValidator:

public class MoneyValidator implements ConstraintValidator<Money, Double> {  
   
    private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式  
    private Pattern moneyPattern = Pattern.compile(moneyReg);  
    private UserController controller;  
     
    public void initialize(Money money) {  
       // TODO Auto-generated method stub  
        
    }  
   
    public boolean isValid(Double value, ConstraintValidatorContext arg1) {  
       // TODO Auto-generated method stub  
       System.out.println("UserController: .............." + controller);  
       if (value == null)  
           returntrue;  
       return moneyPattern.matcher(value.toString()).matches();  
    }  
   
    public UserController getController() {  
       return controller;  
    }  
   
    @Resource  
    public void setController(UserController controller) {  
       this.controller = controller;  
    }  
   
}  








  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值