SpringMVC学习记录--Validator验证分析

来源:互联网 发布:docker 阿里云 编辑:程序博客网 时间:2024/06/08 04:50

一.基于Validator接口的验证.

首先创建User实例,并加入几个属性

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<codeclass="hljs cs">publicclass User {
    privateString username;
    privateString password;
    privateString nickname;
 
    publicString getUsername() {
        returnusername;
    }
 
    publicvoid setUsername(String username) {
        this.username = username;
    }
 
    publicString getPassword() {
        returnpassword;
    }
 
    publicvoid setPassword(String password) {
        this.password = password;
    }
 
    publicString getNickname() {
        returnnickname;
    }
 
    publicvoid setNickname(String nickname) {
        this.nickname = nickname;
    }
 
    @Override
    publicString toString() {
        return"username--"+username+"password--"+password+"nickname--"+nickname;
    }
}</code>

接着创建用于校检的类UserValidator,让其实现Validator,覆盖其中的两个方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<codeclass="hljs java">importmain.java.model.User;
importorg.springframework.validation.Errors;
importorg.springframework.validation.Validator;
 
publicclass UserValidator implementsValidator {
 
 
    @Override
    publicboolean supports(Class aClass) {
        //判断是否是要校验的类,这里是User
        returnUser.class.equals(aClass);
    }
 
    @Override
    publicvoid validate(Object o, Errors errors) {
        User u = (User) o;
        if(null== u.getPassword() || "".equals(u.getPassword())){
            //此方法可以加四个参数,第一个表单域field,
            //区分是哪个表单出错,第二个errorCode错误码,
            //第三个制定了资源文件中占位符,第四个具体错误返回信息
            //简写版可以把2,3参数去掉
            errors.rejectValue("password",null,null,"password is null");
        }
    }
}</code>

上面的类只实现了对密码判断是否为空,为空则注册这一错误信息,也就是”password is null”,接下来要实现控制器,控制器要做的事情,第一是注册这个校验器,第二是实现校验.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<codeclass="hljs java">importmain.java.model.User;
......
 
/**
 * 加上@Controller决定这个类是一个控制器
 */
@Controller
@RequestMapping("/user")
publicclass HelloController {
 
    //我们知道在Controller类中通过@InitBinder标记的方法只有在请求当前Controller的时候才会被执行
    //所以在这里注册校验器
    @InitBinder
    publicvoid initBainder(DataBinder binder){
        binder.replaceValidators(newUserValidator());
 
    }
    //这个方法主要是跳转到登录页面
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    publicString login(Model model){
        model.addAttribute(newUser());
        return"user/login";
    }
    //处理登录表单
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    publicString login(@ValidatedUser user, BindingResult br){
 
        if(br.hasErrors()){
            return"user/login";
        }
        return"--";
    }
    }</code>

上面代码可以看到@Validated User user, BindingResult br这两个参数,@Validated表明参数user是要校验的类,BindingResult是存储错误信息的类,两者必须一一对应,并且位置挨着,不能中间有其他参数,
最后随便写一个jsp页面实现校检

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<codeclass="hljs xml"><%@ contenttype="text/html;charset=UTF-8"language="java"page="">
<%@taglibprefix="sf"uri="http://www.springframework.org/tags/form">
<%>
    <sf:form method="post"modelattribute="user">
        用户名:<sf:input path="username"></sf:input><sf:errors path="username"></sf:errors>
         
 
        密码:<sf:input path="password"></sf:input><sf:errors path="password"></sf:errors>
         
 
        昵称:<sf:input path="nickname"></sf:input><sf:errors path="nickname"></sf:errors>
         
 
        <input type="submit"value="提交">
    </sf:form></code>
<%@ contenttype="text/html;charset=UTF-8" language="java" page=""><%@taglib prefix="sf" uri="http://www.springframework.org/tags/form"><%>

这里写图片描述

前面实现的是局部校验,只对当前控制器有效,如果要实现全局校验的话需要配置springMVC.xml文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<codeclass="hljs xml">
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"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 validator="userValidator"></mvc:annotation-driven>
 
    <beanclass="com.xxx.xxx.UserValidator"id="userValidator"></bean>
 
    ...
</beans></code>

二.使用Annotaion JSR-303标准的验证

使用这个需要导入支持JSR-303标准的包,建议使用Hibernate Validator这个包,先看这个标准的原生标注

限制说明@Null限制只能为null@NotNull限制必须不为null@AssertFalse限制必须为false@AssertTrue限制必须为true@DecimalMax(value)限制必须为一个不大于指定值的数字@DecimalMin(value)限制必须为一个不小于指定值的数字@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction@Future限制必须是一个将来的日期@Max(value)限制必须为一个不大于指定值的数字@Min(value)限制必须为一个不小于指定值的数字@Past限制必须是一个过去的日期@Pattern(value)限制必须符合指定的正则表达式@Size(max,min)限制字符长度必须在min到max之间@Past验证注解的元素值(日期类型)比当前时间早@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

要使用很简单,在需要验证的变量前面加上该Annotation即可,看下面使用后的User

?
1
2
3
4
5
6
7
8
9
<codeclass="hljs java">publicclass User {
    @NotEmpty(message = "用户名不能为空")
    privateString username;
    @Size(min=6,max= 20,message = "密码长度不符合标准")
    privateString password;
    privateString nickname;
 
   ......
}</code>

然后再控制器里面加入验证就可以了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<codeclass="hljs java">@Controller
@RequestMapping("/user")
publicclass HelloController {
 
 
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    publicString login(Model model){
        model.addAttribute(newUser());
        return"user/login";
    }
 
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    publicString login(@ValidatedUser user, BindingResult br){
 
        if(br.hasErrors()){
            return"user/login";
        }
        return"user/login";
    }
    }</code>

然后jsp页面还是之前的页面,验证效果如下,这种方法明显简单多了
这里写图片描述


3.定义自己的Annotation Validator

这部分直接从大牛那拷贝过来的.

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<codeclass="hljs avrasm">
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
 
importjavax.validation.Constraint;
importjavax.validation.Payload;
 
importcom.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[] payload() default{};
 
}</code>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<codeclass="hljs axapta">
importjava.util.regex.Pattern;
 
importjavax.validation.ConstraintValidator;
importjavax.validation.ConstraintValidatorContext;
 
importcom.xxx.xxx.constraint.Money;
 
publicclass MoneyValidator implementsConstraintValidator {
 
    privateString moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
    privatePattern moneyPattern = Pattern.compile(moneyReg);
 
    publicvoid initialize(Money money) {
       // TODO Auto-generated method stub
 
    }
 
    publicboolean isValid(Double value, ConstraintValidatorContext arg1) {
       // TODO Auto-generated method stub
       if(value == null)
           returntrue;
       returnmoneyPattern.matcher(value.toString()).matches();
    }
 
}</code>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<codeclass="hljs handlebars">@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MinValidator.class)
public@interface Min {
 
    intvalue() default0;
 
    String message();
 
    Class[] groups() default{};
 
    Class[] payload() default{};
}</code>

MinValidator校验器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<codeclass="hljs axapta">publicclass MinValidator implementsConstraintValidator {
 
    privateint minValue;
 
    publicvoid initialize(Min min) {
       // TODO Auto-generated method stub
       //把Min限制类型的属性value赋值给当前ConstraintValidator的成员变量minValue
       minValue = min.value();
    }
 
    publicboolean isValid(Integer value, ConstraintValidatorContext arg1) {
       // TODO Auto-generated method stub
       //在这里我们就可以通过当前ConstraintValidator的成员变量minValue访问到当前限制类型Min的value属性了
       returnvalue >= minValue;
    }
 
}</code>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<codeclass="hljs cs">publicclass User {
 
    privateint age;
 
    privateDouble salary;
 
    @Min(value=8, message="年龄不能小于8岁")
    publicint getAge() {
       returnage;
    }
 
    publicvoid setAge(intage) {
       this.age = age;
    }
 
    @Money(message="标准的金额形式为xxx.xx")
    publicDouble getSalary() {
       returnsalary;
    }
 
    publicvoid setSalary(Double salary) {
       this.salary = salary;
    }
 
}</code>

 

阅读全文
0 0
原创粉丝点击