当前位置 > it书童 > java > 正文

验证框架

java it书童 2021-01-14 16:42:57 0赞 0踩 150阅读 0评论

验证模型

验证模型可分为:

  • 分层验证模型

  • Java Bean 验证模型

Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API

Hibernate Validator 是对 Bean Validation 规范的实现

Spring Validation 在 Hiberante Validator 的基础上,对其进行了二次封装,以满足在 Spring 环境中更简单、高效地对数据进行验证

完成验证的步骤

首先引入相关依赖:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.20.Final</version>
</dependency>

操作步骤可分解为:

  1. 约束注解的定义

  2. 约束验证规则

  3. 约束注解的声明

  4. 约束验证流程

前两步验证框架为我们做好了。如果我们要自定义约束注解,就需要完成整个步骤

如:自定义手机号约束注解

  1. 定义 @interface Phone 注解

  2. 实现约束验证器 PhoneValidator.java

  3. 声明 @Phone 约束验证

  4. 执行手机号约束验证流程

验证框架的综合使用

自定义手机号的约束注解,其他使用预定义的约束注解

自定义注解接口

package efficient.validation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * 自定义手机号约束注解
 */
@Documented
// 注解的作用目标
@Target({ElementType.FIELD})
// 注解的保留策略
@Retention(RetentionPolicy.RUNTIME)
// 与普通注解的不同之处:与约束注解关联的验证器
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {

    // 约束注解验证时的输出信息
    String message() default "手机号校验错误";

    // 约束注解在验证时所属的组别
    Class<?>[] groups() default {};

    // 约束注解的有效负载
    Class<? extends Payload>[] payload() default {};
}

自定义约束注解的校验逻辑

package efficient.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PhoneValidator implements ConstraintValidator<Phone, String> {
    public void initialize(Phone constraint) {
    }

    /**
     * 自定义校验逻辑方法
     *
     * @param value
     * @param context
     * @return
     */
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 手机号验证规则:158开头随便
        String check = "158\\d{8}";
        Pattern regex = Pattern.compile(check);

        // 空值处理
        String phone = Optional.ofNullable(value).orElse("");
        Matcher matcher = regex.matcher(phone);

        return matcher.matches();
    }
}

实体类中使用约束注解:可以区分不同使用场景

package efficient.validation;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.GroupSequence;
import javax.validation.Valid;
import javax.validation.constraints.*;
import javax.validation.groups.Default;
import java.util.Date;
import java.util.List;

/**
 * 待验证对象实体类
 */
@Data
public class UserInfo {
    // 登录场景
    public interface LoginGroup {}

    // 注册场景
    public interface RegisterGroup {}

    // 组排序场景
    @GroupSequence({
        LoginGroup.class,
        RegisterGroup.class,
        Default.class
    })
    public interface Group {}

    @NotNull(
        message = "用户id不能为空",
        groups = LoginGroup.class // 指定使用场景
    )
    private String userId;

    @NotEmpty(message = "用户名称不能为空") // 不会自动去掉前后空格
    private String userName;

    @NotBlank(message = "用户密码不能为空") // 自动去掉字符串前后空格再验证是否为空
    @Length(min = 6, max = 20, message = "密码长度不能少于6位,多于20位")
    private String password;

    @NotNull(
        message = "邮箱不能为空",
        groups = RegisterGroup.class
    )
    @Email(message = "邮箱必须为有效邮箱")
    private String email;

    @Phone(message = "手机号不是158开头的")
    private String phone;

    @Min(value = 18, message = "年龄不能小于18岁")
    @Max(value = 60, message = "年龄不能大于60岁")
    private Integer age;

    @Past(message = "生日不能为未来或当前时间点")
    private Date birthday;

    @Size(min = 1, message = "不能少于1个好友")
    private List<@Valid UserInfo> friends; // 对集合中的 UserInfo 也进行同样的验证
}

服务层中使用约束注解

package efficient.validation;

import javax.validation.Valid;

public class UserInfoService {

    /**
     * UserInfo 作为输入参数
     * @param userInfo
     */
    public void setUserInfo(@Valid UserInfo userInfo) {

    }

    /**
     * UserInfo 作为输出参数
     * @return
     */
    public @Valid UserInfo getUserInfo() {
        return new UserInfo();
    }

    public UserInfoService() {
    }

    public UserInfoService(@Valid UserInfo userInfo) {
    }

}

测试调用结果

package efficient.validation;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Set;

/**
 * 验证测试类
 */
public class ValidationTest {
    // 验证器对象
    private Validator validator;
    // 待验证对象
    private UserInfo userInfo;
    // 验证结果集合
    private Set<ConstraintViolation<UserInfo>> set;
    // 验证结果集合
    private Set<ConstraintViolation<UserInfoService>> otherSet;

    @Before
    public void init() {
        // 初始化验证器
        validator = Validation.buildDefaultValidatorFactory().getValidator();
        // 初始化待验证对象
        userInfo = new UserInfo();
        userInfo.setUserId("001");
        userInfo.setUserName("郭靖");
        userInfo.setPassword("rootPwd");
        userInfo.setEmail("123@qq.com");
        userInfo.setAge(26);
        userInfo.setPhone("13631439097");
        Calendar calendar = Calendar.getInstance();
        calendar.set(2012, 1, 1);
        userInfo.setBirthday(calendar.getTime());
        UserInfo friend = new UserInfo();
        friend.setUserId("002");
        friend.setUserName("黄蓉");
        friend.setPassword("pwdRoot");
        friend.setAge(24);
        userInfo.setFriends(new ArrayList(){{add(friend);}});
    }

    @After
    public void print() {
        if (set != null) {
            set.forEach(item -> {
                // 输出验证
                System.out.println(item.getMessage());
            });
        }
        if (otherSet != null) {
            otherSet.forEach(item -> {
                // 输出验证
                System.out.println(item.getMessage());
            });
        }
    }

    // 初级验证
    @Test
    public void primaryValidation() {
        set = validator.validate(userInfo);
    }

    // 分组验证
    @Test
    public void groupValidation() {
        set = validator.validate(userInfo, UserInfo.RegisterGroup.class);
    }

    // 组排序,当前面的组验证不通过时,中断执行
    @Test
    public void groupSequenceValidation() {
        set = validator.validate(userInfo, UserInfo.Group.class);
    }

    // 对方法输入参数进行约束注解校验
    @Test
    public void paramValidation() throws NoSuchMethodException {
        // 获取校验执行器
        ExecutableValidator executableValidator = validator.forExecutables();

        // 待验证对象
        UserInfoService service = new UserInfoService();
        // 待验证方法
        Method method = service.getClass().getMethod("setUserInfo", UserInfo.class);

        // 方法输入参数
        Object[] paramObjects = new Object[]{new UserInfo()};

        // 对方法的输入参数进行校验
        otherSet = executableValidator.validateParameters(service, method, paramObjects);
    }

    // 对方法返回值进行约束注解校验
    @Test
    public void returnValueValidation() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 获取校验执行器
        ExecutableValidator executableValidator = validator.forExecutables();

        // 待验证对象
        UserInfoService service = new UserInfoService();
        // 待验证方法
        Method method = service.getClass().getMethod("getUserInfo");

        // 调用方法得到返回值
        Object returnValue = method.invoke(service);

        // 校验方法返回值是否符合约束
        otherSet = executableValidator.validateReturnValue(service, method, returnValue);
    }


    // 对构造函数输入参数进行校验
    @Test
    public void constructorValidation() throws NoSuchMethodException {
        // 获取校验执行器
        ExecutableValidator executableValidator = validator.forExecutables();

        // 获取构造函数
        Constructor<UserInfoService> constructor = UserInfoService.class.getConstructor(UserInfo.class);
        Object[] paramObjects = new Object[]{new UserInfo()};

        // 校验构造函数
        otherSet = executableValidator.validateConstructorParameters(constructor, paramObjects);
    }

}
关于我
一个文科出身的程序员,追求做个有趣的人,传播有价值的知识,微信公众号主要分享读书思考心得,不会有代码类文章,非程序员的同学请放心订阅
转载须注明出处:https://www.itshutong.com/articles/1030