zoukankan      html  css  js  c++  java
  • 十、验证框架

    分层验证与JavaBean验证

    分层验证模型

    Bean Validation简介

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

    JCP,JSR简介

    JCP(Java Community Process)成立于1998年,是使有兴趣的各方参与定义Java的特征和未来版本的正式过程。
    JCP使用JSR(Java 规范请求,Java Specification Requests)作为正式规范文档,描述被提议加入到Java体系中的规范和技术

    常用约束注解

    空值校验类

    • @Null 必须为null
    • @NotNull 不能为null,但可以为empty,用在基本类型上
    • @NotEmpty 不能为null,而且长度必须大于0,用在CharSequence子类型、Collection、Map、数组
    • @NotBlank 不能为null,只能作用在String上,而且调用trim()后,长度必须大于0

    范围校验类

    • @Min 必须是一个数字,其值必须大于等于指定的最小值
    • @Max 必须是一个数字,其值必须小于等于指定的最大值
    • @Size 大小必须在指定的范围内,作用字符串、Collection、Map、数组等
    • @Length 长度必须在指定的范围内,作用CharSequence子类型
    • @Digits 必须是一个数字,其值必须在可接受的范围内
    • @Future 必须是一个将来的日期
    • @Past 不能是一个将来的日期
    • @Negative 是不是负数,0不能通过验证

    其他校验类

    • @Email 必须是电子邮件地址
    • @URL 必须为有效的 URL
    • @AssertTrue 必须为true
    • @Pattern 必须符合指定的正则表达式

    依赖

    JSR 380是用于bean验证的Java API的规范,是JavaEE和JavaSE的一部分。
    根据JSR 380规范,validation-api依赖包含标准验证API:

            <!--验证API-->
            <dependency>
                <groupId>javax.validation</groupId>
                <artifactId>validation-api</artifactId>
                <version>2.0.1.Final</version>
            </dependency>
    

    Hibernate Validator是验证API的参考实现。
    要使用它,我们必须添加以下依赖项:

            <!--验证API参考实现-->
            <dependency>
                <groupId>org.hibernate.validator</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>6.1.2.Final</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate.validator</groupId>
                <artifactId>hibernate-validator-annotation-processor</artifactId>
                <version>6.1.2.Final</version>
            </dependency>
    

    JSR 380提供对变量插值的支持,允许在违规消息中使用表达式。

    要解析这些表达式,我们必须在表达式语言API和该API的实现上添加依赖项。GlassFish提供参考实现:

            <!--表达式语言依赖关系-->
            <dependency>
                <groupId>javax.el</groupId>
                <artifactId>javax.el-api</artifactId>
                <version>3.0.1-b06</version>
            </dependency>
            <dependency>
                <groupId>javax.el</groupId>
                <artifactId>javax.el-api</artifactId>
                <version>3.0.1-b06</version>
            </dependency>
    

    中级验证

    级联验证

    • @Valid 指定递归验证关联的对象;如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证
       /**
         * 用户好友列表
         * @Valid验证集合中的对象
         */
        @NotEmpty(message = "好友不能为空")
        @Size(min = 1,message = "好友不能少于一个")
        private List<@Valid UserInfo> friends;
    

    分组验证

    定义接口区分不同场景的分组,然后在在注解验证的groups上指定场景的接口,在需要使用验证的地方指定分组 。

       /**
         * 分组验证
         * 定义两个接口分别对登录和注册分组
         */
        public interface LoginGroup{}
        public interface RegisterGroup{}
    
        /**
         * 用户ID
         */
        @NotNull(message = "用户ID不能为空",groups = RegisterGroup.class)
        private String userId;
        /**
         * 用户名字
         */
        @NotEmpty(message = "用户名称不能为空",groups = LoginGroup.class)
        private String userName;
        /**
         * 用户密码
         */
        @NotBlank(message = "用户密码不能为空" ,groups = {LoginGroup.class,RegisterGroup.class})
        @Length(min = 6,max = 20,message = "密码长度不能小于6位,不能大于20位")
        private String passWord;
    

    组序列

    多个分组指定分组的验证顺序,使用@GroupSequence注解

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

    实例

    /**
     * @author fangliu
     * @date 2020-02-22
     * @description 待验证的Bean对象
     */
    @Data
    public class UserInfo {
        /**
         * 分组验证
         * 定义两个接口分别对登录和注册分组
         */
        public interface LoginGroup{}
        public interface RegisterGroup{}
    
        /**
         * 组序列场景
         * 多个分组指定分组的验证顺序
         */
        @GroupSequence({
                LoginGroup.class,
                RegisterGroup.class,
                Default.class
        })
        public interface Group{}
    
        /**
         * 用户ID
         */
        @NotNull(message = "用户ID不能为空",groups = RegisterGroup.class)
        private String userId;
        /**
         * 用户名字
         */
        @NotEmpty(message = "用户名称不能为空",groups = LoginGroup.class)
        private String userName;
        /**
         * 用户密码
         */
        @NotBlank(message = "用户密码不能为空" ,groups = {LoginGroup.class,RegisterGroup.class})
        @Length(min = 6,max = 20,message = "密码长度不能小于6位,不能大于20位")
        private String passWord;
        /**
         * 用户邮箱
         */
        @NotNull(message = "邮箱不能为空",groups = RegisterGroup.class)
        @Email(message = "邮箱必须为有效邮箱")
        private String email;
        /**
         * 用户手机号
         */
        private String phone;
        /**
         * 用户年龄
         */
        @NotNull(message = "年龄不能为空")
        @Min(value = 18,message = "年龄不能小于18岁")
        @Max(value = 60,message = "年龄不能大于60岁")
        private Integer age;
        /**
         * 用户生日
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        @Past(message = "不能是一个将来的时间")
        private Date birthday;
        /**
         * 用户好友列表
         * @Valid验证集合中的对象
         */
        @NotEmpty(message = "好友不能为空")
        @Size(min = 1,message = "好友不能少于一个")
        private List<@Valid UserInfo> friends;
    
    
    }
    
    /**
     * @author fangliu
     * @date 2020-02-22
     * @description validation验证的测试类
     */
    public class ValidationTest {
        //验证器对象
        private Validator validator;
        //待验证对象
        private UserInfo userInfo;
        //验证错误的结果集合
        private Set<ConstraintViolation<UserInfo>> set;
        /**
         * 初始化操作
         */
        @Before
        public void init(){
            // 初始化验证器
            validator = Validation.buildDefaultValidatorFactory().getValidator();
            // 初始化验证对象
            userInfo = new UserInfo();
            //userInfo.setUserId("1111");
            userInfo.setUserName("张三");
            userInfo.setPassWord("123567");
            userInfo.setEmail("6666666@qq.com");
            userInfo.setAge(18);
           /* LocalDateTime time = LocalDateTime.now();
            Instant instant = time.atZone(ZoneId.systemDefault()).toInstant();
            Date date1 = Date.from(instant);*/
    
            LocalDate localDate = LocalDate.of(2020, 1, 5);
            ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
            Instant instant1 = zonedDateTime.toInstant();
            Date date2 = Date.from(instant1);
            userInfo.setBirthday(date2);
            userInfo.setFriends(new ArrayList(){
                {
                    add(userInfo);
                }
            });
        }
    
        /**
         * 验证结果
         */
        @After
        public void print(){
            // 输出错误信息
            set.forEach(info->
                System.out.println(info.getMessage())
            );
        }
    
        /**
         * 验证结果
         */
        @Test
        public void userTest(){
            // 使用验证器对对象进行验证
            set = validator.validate(userInfo);
        }
    
        /**
         * 分组验证结果
         */
        @Test
        public void groupTest(){
            // 使用验证器对对象进行验证
            set = validator.validate(userInfo,UserInfo.RegisterGroup.class);
        }
    
        /**
         * 分组排序验证结果
         */
        @Test
        public void groupSequenceTest(){
            // 使用验证器对对象进行验证
            set = validator.validate(userInfo,UserInfo.Group.class);
        }
    }
    

    高级校验

    校验参数

    校验返回值

    校验构造方法

    实例

    /**
     * @author fangliu
     * @date 2020-02-22
     * @description 用户信息服务类
     */
    public class UserInfoService {
        /**
         * UserInfo作为输入参数
         * @param userInfo 输入参数
         */
        public void setUserInfo(@Valid UserInfo userInfo){
    
        }
    
        /**
         * UserInfo作为输出参数
         * @return
         */
        public @Valid UserInfo getUserInfo(){
            return new UserInfo();
        }
    
        /**
         * 无参数构造函数
         */
        public UserInfoService() {
        }
    
        /**
         * 接受UserInfo作为参数的构造函数
         * @param userInfo
         */
        public UserInfoService(@Valid UserInfo userInfo) {
        }
    }
    
    /**
     * @author fangliu
     * @date 2020-02-22
     * @description validation验证的测试类
     */
    public class ValidationTest {
        //验证器对象
        private Validator validator;
        //待验证对象
        private UserInfo userInfo;
        //验证错误的结果集合
        private Set<ConstraintViolation<UserInfoService>> set1;
        /**
         * 初始化操作
         */
        @Before
        public void init(){
            // 初始化验证器
            validator = Validation.buildDefaultValidatorFactory().getValidator();
            // 初始化验证对象
            userInfo = new UserInfo();
            //userInfo.setUserId("1111");
            userInfo.setUserName("张三");
            userInfo.setPassWord("123567");
            userInfo.setEmail("6666666@qq.com");
            userInfo.setAge(18);
           /* LocalDateTime time = LocalDateTime.now();
            Instant instant = time.atZone(ZoneId.systemDefault()).toInstant();
            Date date1 = Date.from(instant);*/
    
            LocalDate localDate = LocalDate.of(2020, 1, 5);
            ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
            Instant instant1 = zonedDateTime.toInstant();
            Date date2 = Date.from(instant1);
            userInfo.setBirthday(date2);
            userInfo.setFriends(new ArrayList(){
                {
                    add(userInfo);
                }
            });
        }
    
        /**
         * 验证结果
         */
        @After
        public void print(){
            set1.forEach(info->
                System.out.println(info.getMessage())
            );
        }
        /**
         * 对方法输入参数进行校验
         * 在实现方法入参处使用@Valid
         */
        @Test
        public void paramsValidationTest() throws NoSuchMethodException {
            // 获取校验执行器
            ExecutableValidator executableValidator = validator.forExecutables();
            // 待验证对象
            UserInfoService service = new UserInfoService();
            // 待验证方法
            Method method = service.getClass().getMethod("setUserInfo", UserInfo.class);
            // 方法输入参数
            Object[] paramObjects = {new UserInfo()};
            // 对方法的输入进行参数校验
            set1 = executableValidator.validateParameters(service, method, paramObjects);
        }
    
        /**
         * 对方法对返回值进行约束校验
         * 在实现方法返回值处使用@Valid
         */
        @Test
        public void returnValidationTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            // 获取校验执行器
            ExecutableValidator executableValidator = validator.forExecutables();
            // 待验证对象
            UserInfoService service = new UserInfoService();
            // 待验证方法
            Method method = service.getClass().getMethod("getUserInfo");
            // 调用方法得到返回值
            Object returnValue = method.invoke(service);
            // 校验方法返回值是否符合约束
            set1 = executableValidator.validateReturnValue(service, method, returnValue);
        }
    
    
        /**
         * 对构造函数的入参进行校验
         * 在构造方法入参处使用@Valid
         */
        @Test
        public void constructorTest() throws NoSuchMethodException {
            // 获取校验执行器
            ExecutableValidator executableValidator = validator.forExecutables();
            // 待验证方法
            Constructor constructor = UserInfoService.class.getConstructor(UserInfo.class);
            // 方法输入参数
            Object[] paramObjects = {new UserInfo()};
            // 校验构造函数的入参是否符合约束
            set1 = executableValidator.validateConstructorParameters(constructor,paramObjects);
        }
    }
    

    自定义手机号约束注解

    1. 自定义校验逻辑方法

    /**
     * @author fangliu
     * @date 2020-02-23
     * @description 自定义手机号约束注解关联验证器
     */
    public class PhoneValidator implements ConstraintValidator<Phone,String> {
    
        /**
         * 自定义校验逻辑方法
         * @param s
         * @param constraintValidatorContext
         * @return
         */
        @Override
        public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
            // 手机号校验规则:158开始
            String check = "158\d{8}";
            Pattern regex = Pattern.compile(check);
            String phone = Optional.ofNullable(s).orElse("");
            Matcher matcher = regex.matcher(phone);
            return matcher.matches();
        }
    }
    

    2. 自定义手机号约束注解

    /**
     * 自定义手机号约束注解
     */
    @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 {};
    }
    

    3. 使用自定义注解

        /**
         * 用户手机号
         */
        @Phone(message = "手机号以158开始")
        private String phone;
    
  • 相关阅读:
    Linux之find命令
    Android WebView如何加载assets下的html文件
    Android 静默安装
    Android listview下拉刷新 SwipeRefreshLayout
    AndroidManifest.xml 详解
    Android 查看内存使用状况
    Android invalidate() 和 postInvalidate()的区别
    Android动画之Interpolator和AnimationSet
    实现Fragment的切换和ViewPager自动循环设置切换时间
    android 实现橡皮擦效果以及保存涂鸦的功能
  • 原文地址:https://www.cnblogs.com/bigfairy/p/14002103.html
Copyright © 2011-2022 走看看