两种方式实现Spring 业务验证

举报
cxuan 发表于 2021/07/15 00:27:34 2021/07/15
【摘要】 关注公众号回复002,有你想要的一切 这是 cxuan 的第 33 篇原创文章 验证在任何时候都非常关键。考虑将数据验证作为业务逻辑开发有利也有弊,Spring 认为,验证不应该只在Web 端进行处理,在服务端也要进行相应的处理,可以防止脏数据存入数据库中,从而避免为运维同学和测试同学造成更...

640

关注公众号回复002,有你想要的一切

640?wx_fmt=jpeg

这是 cxuan 的第 33 篇原创文章

验证在任何时候都非常关键。考虑将数据验证作为业务逻辑开发有利也有弊,Spring 认为,验证不应该只在Web 端进行处理,在服务端也要进行相应的处理,可以防止脏数据存入数据库中,从而避免为运维同学和测试同学造成更大的困扰,因为数据造成的bug会更加难以发现,而且开发人员关注点也不会放在数据本身的问题上,所以做服务端的验证也是非常有必要的。考虑到上面这些问题,Spring 提供了两种主要类型的验证:

  • 一个是实现Validator 接口来创建自定义验证器,用于服务端数据校验。

  • 一种是通过Spring 对Bean Validation 支持实现的。

通过使用 Spring Validator 接口进行验证

Spring 提供 Validator 接口用于验证对象。Validator 接口通过使用 Errors 对象来工作,以便在验证时,验证器可以向 Errors 对象报告验证失败。下面是一个简单的 对象示例


    
  1. public class Person {
  2. private String name;
  3. private int age;
  4. // get and set...
  5. }

下面一个例子为 Person 对象提供了一种验证方式,通过实现了 org.springframework.validation.Validator 接口 的两个方法:

  • supports(Class): 表示此 Validator 是否能够验证提供的类的实例

  • validate(Object, org.springframework.validation.Errors): 验证给定的对象,如果验证错误,则注册具有给定 Errors 对象。

实现一个 Validator 非常简单,而且Spring 也提供了 ValidationUtils 工具类帮助进行验证。下面是一个验证 Person 对象的例子:


    
  1. @Component
  2. public class PersonValidator implements Validator {
  3. // 此 Validator 只验证 Person 实例
  4. public boolean supports(Class clazz) {
  5. return Person.class.equals(clazz);
  6. }
  7. public void validate(Object obj, Errors e) {
  8. ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
  9. }
  10. }

上面代码示例中的静态方法 rejectIfEmpty() 方法用于拒绝name属性,当name 属性是 null 或者是 空串的时候。查看 ValidationUtils 文档关于它能够提供的功能。

然后再来编写配置类 AppConfig:


    
  1. @Configuration
  2. @ComponentScan("com.spring.validation")
  3. public class AppConfig {}

配置@ComponentScan 注解用于自动装配,默认是使用 basePackages 扫描指定包,字符串表示。

然后对上面的程序进行验证


    
  1. public class SpringValidationApplicationTests {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
  4. Person person = new Person();
  5. person.setAge(18);
  6. person.setName(null);
  7. PersonValidator personValidator = applicationContext.getBean("personValidator", PersonValidator.class);
  8. BeanPropertyBindingResult result = new BeanPropertyBindingResult(person,"cxuan");
  9. ValidationUtils.invokeValidator(personValidator,person,result);
  10. List<ObjectError> allErrors = result.getAllErrors();
  11. allErrors.forEach(e-> System.out.println(e.getCode()));
  12. }
  13. }

因为是基于注解的配置,所以使用 AnnotationConfigApplicationContext上下文启动类,把配置类 AppConfig 当作参数,然后构建一个Person 类,为了测试验证有效性,把 name 设置为 null,然后通过上下问的 getBean 方法获得 personValidator 的实例,通过使用 BeanPropertyBindingResult 把 person 绑定为 cxuan 的名字,然后使用 ValidationUtils 工具类进行验证,最后把验证的结果进行检查。

上面程序经验证后的结果如下:

org.springframework.validation.ValidationUtils - Invoking validator [com.spring.validation.PersonValidator@37918c79] DEBUG org.springframework.validation.ValidationUtils - Validator found 1 errors name.empty

使用 Bean Validation 进行验证

从 Spring4 开始,就已经实现对 JSR-349 Bean Validation 的全面支持。Bean Validation API 在 javax.validation.constraints 包中以 Java 注解(例如 @NonNull) 形式定义了一组可用域对象的约束。

通过使用 Bean Validation API ,可以避免耦合到特定的验证服务提供程序。Spring 对 Bean Validation API 提供了无缝支持,主要使用一些注解进行验证,下面一起来看一下

定义对象属性上的验证约束

首先,将验证约束应用于域对象属性。使用maven 配置需要引入对应的依赖


    
  1. <dependency>
  2. <groupId>javax.validation</groupId>
  3. <artifactId>validation-api</artifactId>
  4. <version>1.1.0.Final</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.hibernate</groupId>
  8. <artifactId>hibernate-validator</artifactId>
  9. <version>5.2.4.Final</version>
  10. </dependency>

之后定义了一些实体类,使用 javax.validation.constraints 包中的注释进行标注


    
  1. public class Singer {
  2. @NotNull
  3. @Size(min = 2,max = 60)
  4. private String firstName;
  5. private String lastName;
  6. @NotNull
  7. private Genre genre;
  8. private Gender gender;
  9. get and set...
  10. }

对于 firstName ,定义了两个约束,第一个约束由 @NotNull 进行控制,它表示该值不能为空。此外,@Size注解控制着 firstName 的长度在 2 - 60 之间。@NotNull 还用于 genre 属性。下面是GenreGender 的枚举类


    
  1. public enum Genre {
  2. POP("P"),
  3. JAZZ("J"),
  4. BLUES("B"),
  5. COUNTRY("C");
  6. private String code;
  7. private Genre(String code){
  8. this.code = code;
  9. }
  10. public String toString(){
  11. return this.code;
  12. }
  13. }
  14. public enum Gender {
  15. MALE("M"),
  16. FEMALE("F");
  17. private String code;
  18. Gender(String code){
  19. this.code = code;
  20. }
  21. @Override
  22. public String toString() {
  23. return this.code;
  24. }
  25. }

Genre 表示歌手所属的音乐类型,而 Gender 与音乐事业不相关,所以可以为空

在 Spring 中配置 Bean Validation 支持

为了在 Spring 的 ApplicationContext 中配置对 Bean Validation API 的支持,可以在Spring 的配置中定义一个 LocalValidatorFactoryBean 的 bean如下


    
  1. @Configuration
  2. @ComponentScan("com.spring.validation")
  3. public class ValidationConfig {
  4. @Bean
  5. LocalValidatorFactoryBean validatorFactoryBean(){
  6. return new LocalValidatorFactoryBean();
  7. }
  8. }

声明一个 LocalValidatorFactoryBean 的 bean 是必须的。默认情况下,Spring 会在类路径下搜索 Hibernate Validator库,验证它是否存在。

下面我们编写一个为 Singer 类提供验证服务的服务类


    
  1. @Service
  2. public class SingerValidationService {
  3. @Autowired
  4. private Validator validator;
  5. public Set<ConstraintViolation<Singer>> validateSinger(Singer singer){
  6. return validator.validate(singer);
  7. }
  8. }

注入一个 javax.validation.Validator 实例(请注意与 Spring 提供的 Validator 接口不同)。一旦定义了 LocalValidatorFactoryBean ,就可以在应用程序中的任意位置创建 Validator 的句柄。要在 POJO 上进行验证,需要调用 validator.validate 方法,验证结果以 ConstraintViolation<T> 接口的集合形式返回。下面是上面例子程序的验证


    
  1. public class SpringBeanValidationTest {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ValidationConfig.class);
  4. SingerValidationService singerBean = applicationContext.getBean(SingerValidationService.class);
  5. Singer singer = new Singer();
  6. singer.setFirstName("c");
  7. singer.setLastName("xuan");
  8. singer.setGenre(null);
  9. singer.setGender(null);
  10. validateSinger(singer,singerBean);
  11. applicationContext.close();
  12. }
  13. private static void validateSinger(Singer singer,SingerValidationService singerValidationService){
  14. Set<ConstraintViolation<Singer>> violationSet = singerValidationService.validateSinger(singer);
  15. listViolations(violationSet);
  16. }
  17. private static void listViolations(Set<ConstraintViolation<Singer>> violations){
  18. System.out.println("violations.size() = " + violations.size());
  19. for(ConstraintViolation<Singer> violation : violations){
  20. System.out.println("Validation error for property : " + violation.getPropertyPath());
  21. System.out.println("with value : " + violation.getInvalidValue());
  22. System.out.println("with error message : " + violation.getMessage());
  23. }
  24. }
  25. }

上述代码构建了一个 Singer 类进行验证,因为 firstname 属性的要求是长度介于 2 - 60 之间并且不能为null,所以这里只用了一个字符验证,genre 属性不能为null,最核心的验证方法就是 singerValidationService.validateSinger(singer).方法,它会调用


    
  1. public Set<ConstraintViolation<Singer>> validateSinger(Singer singer){
  2. return validator.validate(singer);
  3. }

进行验证,验证的结果返回的是 ConstraintViolation<Singler>类型,然后把对应的错误信息输出,上面的错误信息是

violations.size() = 2 Validation error for property : firstName with value : c with error message : 个数必须在2和60之间 Validation error for property : genre with value : null with error message : 不能为null

可以打印出两个错误,并输出错误的属性、值以及错误信息。

640?wx_fmt=png
你点的每个好看,我都认真当成了喜欢

文章来源: cxuan.blog.csdn.net,作者:程序员cxuan,版权归原作者所有,如需转载,请联系作者。

原文链接:cxuan.blog.csdn.net/article/details/102634176

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。