BeanValidation

BeanValidation(JSR-303)是JavaEE中的JavaBean数据校验规范,用于简化和标准化数据验证过程。它允许开发人员在代码中使用注解来声明验证规则,并在运行时对这些规则进行验证。如果不使用数据校验框架,我们对用户的输入需要编写大量if/else进行判断其输入是否合法,这类代码占用大量篇幅,而且不方便复用。BeanValidation提供的声明式校验则能很好的解决这个问题。

使用BeanValidation非常简单,我们为实体类中需要校验的字段标注注解,其中声明了如何校验以及相关参数,然后通过验证器校验对象中的字段即可。

引入相关依赖

BeanValidation的标准接口包含在JavaEE SDK中,如果我们的工程最终部署到JavaEE应用服务器,引入以下依赖即可。不过注意该依赖并没有包含BeanValidation的具体实现,它通常由JavaEE应用服务器提供。

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0.1</version>
    <scope>provided</scope>
</dependency>

不过如果不使用JavaEE平台,我们也可以单独引入相关的规范和实现框架。Hibernate Validator是BeanValidation规范的一个主流实现,对于普通Java工程,我们可以单独引入这两个包来使用BeanValidation的功能。另外Hibernate Validator又依赖于JavaEE的EL规范和其实现,因此我们还需要手动引入javax.el-api和其实现框架,这里我们选择GlassFish的实现。

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>
<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</dependency>

注意:hibernate-validator在规范的基础上扩展了BeanValidation,其本身也自带了一些注解可供使用,使用时要注意区分。

使用数据验证注解

使用验证框架进行验证非常简单,一般只需要两步:

  1. 在实体类上标注注解
  2. 调用验证器获得验证结果

我们直接看一个例子。

Student.java

package com.gacfox.demo.model;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;

public class Student {
    @NotBlank(message = "姓名不能为空")
    private String name;
    @Min(value = 8, message = "入学年龄不得小于8岁")
    @Max(value = 15, message = "入学年龄不得大于15岁")
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

Main.java

package com.gacfox.demo;

import com.gacfox.demo.model.Student;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("汤姆");
        student.setAge(16);

        try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) {
            Validator validator = validatorFactory.getValidator();
            // 进行验证
            Set<ConstraintViolation<Student>> validateResults = validator.validate(student);
            // 打印验证结果
            if (validateResults != null && !validateResults.isEmpty()) {
                for (ConstraintViolation<Student> result : validateResults) {
                    System.out.println("Field: [" + result.getPropertyPath() + "] Message:[" + result.getMessage() + "]");
                }
            }
        }
    }
}

执行结果如下。

Field: [age] Message:[入学年龄不得大于15岁]

注意:如果有多个验证错误,它们会全部输出。

代码中,我们的实体类Student上标注了若干注解,其中指定了验证规则和值,以及发生错误时输出的信息,然后在main()方法中获取了验证器并进行了数据校验。实际开发中,我们可以基于验证器API进行封装,便于使用。

BeanValidation内置的验证注解

下面列表中的是一些常用的注解。

注解名 说明
@Null 对象,为空
@NotNull 对象,不为空
@AssertTrue 布尔,为True
@AssertFalse 布尔,为False
@Min(value) 数字,最小为value
@Max(value) 数字,最大为value
@Positive 数字,正数
@PositiveOrZero 数字,正数或0
@Negative 数字,负数
@NegativeOrZero 数字,负数或0
@Pattern(value) 字符串,正则校验
@Email 字符串,邮箱类型
@NotBlank 字符串,不为空字符串
@Size(max, min) 字符串、数组或集合,长度范围为min到max
@NotEmpty 集合,不为空
@Past 日期,过去的日期
@Future 日期,将来的日期
@PastOrPresent 日期,过去或者现在
@FutureOrPresent 日期,将来或者现在

自定义验证注解

自定义验证注解也十分简单,我们仿照原版注解定义即可。下面例子中,我们定义了一个验证字符串是否为CamelCase的注解。

CamelCase.java

package com.gacfox.demo.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CamelCaseValidator.class)
public @interface CamelCase {
    String message() default "指定字符串不是CamelCase格式";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

CamelCaseValidator.java

package com.gacfox.demo.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class CamelCaseValidator implements ConstraintValidator<CamelCase, String> {

    private static final String PATTERN = "([a-z]+[A-Z]+\\w+)+";

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return s.matches(PATTERN);
    }
}

代码中,我们的自定义注解指定了一个自定义的验证器类,其中包含了真正的验证代码。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。