惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
Y
Y Combinator Blog
I
InfoQ
美团技术团队
罗磊的独立博客
B
Blog RSS Feed
GbyAI
GbyAI
小众软件
小众软件
IT之家
IT之家
Engineering at Meta
Engineering at Meta
Blog — PlanetScale
Blog — PlanetScale
V
V2EX
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
MyScale Blog
MyScale Blog
博客园 - 聂微东
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss

博客园 - Matt_Cheng

AI编码实践 对于动态注册的bean,aop会生效吗? LLM VSCode快捷命令 图平台技术点 常用CMD命令 jupyter安装与使用 数据结构文章收藏 使用beeline访问hive Java线程异常处理 属性文件加载 node.js待实践项目 spring boot问题记录 docker命令 IDEA环境配置与快捷命令 Spring MVC 使用介绍(十五)数据验证 (二)依赖注入与方法级别验证 Spring MVC 使用介绍(十四)文件上传下载 2018年年终总结
Spring MVC 使用介绍(十六)数据验证 (三)分组、自定义、...
Matt_Cheng · 2019-04-12 · via 博客园 - Matt_Cheng

一、概述

除了依赖注入、方法参数,Bean Validation 1.1定义的功能还包括:

1、分组验证

2、自定义验证规则

3、类级别验证

4、跨参数验证

5、组合多个验证注解

6、其他

二、分组验证

通过分组,可实现不同情况下的不同验证规则,示例如下:

1、定义分组接口

public interface AddView { 
}
public interface UpdateView {
}

2、定义实体

public class Person {
    @Null(groups = AddView.class)
    @NotNull(groups = {UpdateView.class, Default.class})
    private Integer id;
    ...
}

  注:不指定分组,即为Default分组

3、业务类

@Service
@Validated
public class PersonService {
    @Validated(AddView.class)
    public void addPerson(@Valid Person person) {}
    
    @Validated({UpdateView.class})
    public void updatePerson(@Valid Person person) {}
    
    public void defaultOp(@Valid Person person) {}
}

4、测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-context.xml")
public class ValidTest {
    @Autowired
    private PersonService testService;

    @Test(expected = ConstraintViolationException.class)
    public void test1() {
        Person person = new Person();
        person.setId(12);
        testService.addPerson(person);
    }
    
    @Test(expected = ConstraintViolationException.class)
    public void test2() {
        Person person = new Person();
        testService.updatePerson(person);
    }
    
    @Test(expected = ConstraintViolationException.class)
    public void test3() {
        Person person = new Person();
        testService.defaultOp(person);
    }
}

三、自定义验证规则

系统预定义的验证注解不能满足需求时,可自定义验证注解,示例如下:

1、自定义注解

@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = ForbiddenValidator.class)
@Documented
public @interface Forbidden {
    //默认错误消息
    String message() default "{forbidden.word}";

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

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

    //指定多个时使用
    @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        Forbidden[] value();
    }
}

2、自定义注解处理类

public class ForbiddenValidator implements ConstraintValidator<Forbidden, String> {
    private String[] forbiddenWords = {"admin"};

    @Override
    public void initialize(Forbidden constraintAnnotation) {
        //初始化,得到注解数据
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(StringUtils.isEmpty(value)) {
            return true;
        }

        for(String word : forbiddenWords) {
            if(value.contains(word)) {
                return false;//验证失败
            }
        }
        return true;
    }
}

3、默认错误消息

# format.properties
forbidden.word=包含敏感词汇

4、实体类

public class Person {
  @Forbidden
  private String name;
  ...
}

5、业务类

@Service
@Validated
public class PersonService {
    public void defaultOp(@Valid Person person) {
        
    }
}

6、测试

@Test(expected = ConstraintViolationException.class)
public void test4() {
    Person person = new Person();
    person.setName("admin");
    testService.defaultOp(person);
}

四、类级别验证

定义类级别验证,可实现对象中的多个属性组合验证,示例如下:

1、定义注解

@Target({ TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = CheckPasswordValidator.class)
public @interface CheckPassword {
    //默认错误消息
    String message() default "";

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

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

    //指定多个时使用
    @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        CheckPassword[] value();
    }
}

2、定义处理类

public class CheckPasswordValidator implements ConstraintValidator<CheckPassword, Person> {
    @Override
    public void initialize(CheckPassword constraintAnnotation) {
    }

    @Override
    public boolean isValid(Person person, ConstraintValidatorContext context) {
        if(person == null) {
            return true;
        }

        //没有填密码
        if(!StringUtils.isEmpty(person.getNewPassword())) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{password.null}")
                    .addPropertyNode("password")
                    .addConstraintViolation();
            return false;
        }

        if(!StringUtils.isEmpty(person.getConfirmPassword())) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{password.confirmation.null}")
                    .addPropertyNode("confirmation")
                    .addConstraintViolation();
            return false;
        }

        //两次密码不一样
        if (!person.getNewPassword().equals(person.getConfirmPassword())) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{password.confirmation.error}")
                    .addPropertyNode("confirmation")
                    .addConstraintViolation();
            return false;
        }
        return true;
    }
}

3、实体

@CheckPassword
public class Person {
    private String newPassword;
    private String confirmPassword;
    ...
}

4、业务类

@Service
@Validated
public class PersonService {
    public void checkClassValidation(@Valid Person person) {
        
    }
}

5、测试

@Test(expected = ConstraintViolationException.class)
public void test4() {
    Person person = new Person();
    person.setNewPassword("asd");
    person.setConfirmPassword("12132");
    testService.checkClassValidation(person);
}

五、跨参数验证

使用跨参数验证,可实现方法级别中的多个参数组合验证,示例如下:

1、定义注解

@Constraint(validatedBy = CrossParameterValidator.class)
@Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface CrossParameter {
    String message() default "{password.confirmation.error}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

2、定义处理类

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class CrossParameterValidator implements ConstraintValidator<CrossParameter, Object[]> {
    @Override
    public void initialize(CrossParameter constraintAnnotation) {
    }

    @Override
    public boolean isValid(Object[] value, ConstraintValidatorContext context) {
        if(value == null || value.length != 2) {
            throw new IllegalArgumentException("must have two args");
        }
        if(value[0] == null || value[1] == null) {
            return true;
        }
        if(value[0].equals(value[1])) {
            return true;
        }
        return false;
    }
}

3、业务类

@Service
@Validated
public class PersonService {
    @CrossParameter
    public void checkParaValidation(String pw1, String pw2) {
        
    }
}

4、测试

@Test(expected = ConstraintViolationException.class)
public void test5() {
    testService.checkParaValidation("asd", "123");
}

六、组合多个验证注解

可将多个注解组合成一个注解,示例如下:

1、定义注解

@Target({ FIELD})
@Retention(RUNTIME)
@Documented
@NotNull
@Min(1)
@Constraint(validatedBy = { })
public @interface NotNullMin {
    String message() default "";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

2、实体

public class Person {
    @NotNullMin
    private Integer id;
    ...
}

3、业务类

@Service
@Validated
public class PersonService {
    public void checkCompositionValidation(@Valid Person person) {
        
    }
}

4、测试

@Test(expected = ConstraintViolationException.class)
public void test6() {
    Person person = new Person();
    testService.checkCompositionValidation(person);
}

七、其他

Bean Validation 1.1还支持本地化、脚本验证器,详细见参考文档

参考:

Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC