详述Spring对Bean Validation支持的核心API:Validator、SmartValidator、LocalValidatorFactoryBean...【享学Spring】(上)

简介: 详述Spring对Bean Validation支持的核心API:Validator、SmartValidator、LocalValidatorFactoryBean...【享学Spring】(上)

前言


浩浩荡荡的把一般程序员都不太关注的Bean Validation话题讲了这么久,期间小伙伴wx我说一直还没看到他最想看到的内容,我问最想看到啥?他说显然是数据校验在Spring中的使用啊。我想若不出意外,这应该是众多小伙伴的共同心声吧,但路漫漫其修远兮,也得上下求索,本文将切入到最关心的Spring中来~


要想深入了解Spring对Bean Validation的支持,org.springframework.validation.beanvalidation这个包里面的这几个关键API必须搞明白喽,这样再使用起@Valid结合Spring时时才能更加的收放自如~


说明:这个包所在的jar是spring-context,属于Spring上下文的核心功能模块


我把这个包内的类图截图如下,供以参考


image.png


Spring虽然没有直接实现Bean校验这块的JSR规范,但是从Spring3.0开始,Spring就提供了对Bean Validation的支持。


  1. 3.0提供了Bean级别的校验
  2. 3.1提供了更加强大的方法级别的校验


BeanValidationPostProcessor


它就是个普通的BeanPostProcessor。它能够去校验Spring容器中的Bean,从而决定允不允许它初始化完成。


比如我们有些Bean某些字段是不允许为空的,比如数据的链接,用户名密码等等,这个时候用上它处理就非常的优雅和高级了~


若校验不通过,在违反约束的情况下就会抛出异常,阻止容器的正常启动~


public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
  // 这就是我们熟悉的校验器
  // 请注意这里是javax.validation.Validator,而不是org.springframework.validation.Validator
  @Nullable
  private Validator validator;
  // true:表示在Bean初始化之后完成校验
  // false:表示在Bean初始化之前就校验
  private boolean afterInitialization = false;
  ... // 省略get/set
  // 由此可见使用的是默认的校验器(当然还是Hibernate的)
  @Override
  public void afterPropertiesSet() {
    if (this.validator == null) {
      this.validator = Validation.buildDefaultValidatorFactory().getValidator();
    }
  }
  // 这个实现太简单了~~~
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (!this.afterInitialization) {
      doValidate(bean);
    }
    return bean;
  }
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (this.afterInitialization) {
      doValidate(bean);
    }
    return bean;
  }
  protected void doValidate(Object bean) {
    Assert.state(this.validator != null, "No Validator set");
    Object objectToValidate = AopProxyUtils.getSingletonTarget(bean);
    if (objectToValidate == null) {
      objectToValidate = bean;
    }
    Set<ConstraintViolation<Object>> result = this.validator.validate(objectToValidate);
    // 拼接错误消息最终抛出
    if (!result.isEmpty()) {
      StringBuilder sb = new StringBuilder("Bean state is invalid: ");
      for (Iterator<ConstraintViolation<Object>> it = result.iterator(); it.hasNext();) {
        ConstraintViolation<Object> violation = it.next();
        sb.append(violation.getPropertyPath()).append(" - ").append(violation.getMessage());
        if (it.hasNext()) {
          sb.append("; ");
        }
      }
      throw new BeanInitializationException(sb.toString());
    }
  }
}


这个BeanValidationPostProcessor实现的功能确实非常的简单,无非就是对所有的Bean在初始化前/后进行校验。

我们若是对Spring Bean想做约束的话(比如对属性、构造器等等),使用它就非常的方便~


备注:BeanValidationPostProcessor默认可是没有被装配进容器的~


org.springframework.validation.Validator


应用程序特定对象的验证器,这是Spring自己的抽象,注意区别于javax.validation.Validator。这个接口完全脱离了任何基础设施或上下文,也就是说,它没有耦合到只验证Web层、数据访问层或任何层中的对象。它支持应用于程序内的任何层


// 注意:它可不是Spring3后才推出的  最初就有
public interface Validator {
  // 此clazz是否可以被validate
  boolean supports(Class<?> clazz);
  // 执行校验,错误消息放在Errors 装着
  // 可以参考ValidationUtils这个工具类,它能帮助你很多
  void validate(Object target, Errors errors);
}


它的继承树如下:

image.png


SmartValidator


这个子接口它扩展增加了校验分组:hints。


// @since 3.1  这个出现得比较晚
public interface SmartValidator extends Validator {
  // 注意:这里的Hints最终都会被转化到JSR的分组里去~~
  // 所以这个可变参数,传接口Class对象即可~
  void validate(Object target, Errors errors, Object... validationHints);
  // @since 5.1  简单的说,这个方法子类请复写 否则不能使用
  default void validateValue(Class<?> targetType, String fieldName, @Nullable Object value, Errors errors, Object... validationHints) {
    throw new IllegalArgumentException("Cannot validate individual value for " + targetType);
  }
}


SpringValidatorAdapter:校验适配器(重要)


这个实现类Class是非常重要的,它是javax.validation.Validator到Spring的Validator的适配,通过它就可以对接到JSR的校验器来完成校验工作了~


在Spring5.0后,此实现类已完美支持到Bean Validation 2.0


// @since 3.0
public class SpringValidatorAdapter implements SmartValidator, javax.validation.Validator {
  // 通用的三个约束注解都需要有的属性
  private static final Set<String> internalAnnotationAttributes = new HashSet<>(4);
  static {
    internalAnnotationAttributes.add("message");
    internalAnnotationAttributes.add("groups");
    internalAnnotationAttributes.add("payload");
  }
  // 最终都是委托给它来完成校验的~~~
  @Nullable
  private javax.validation.Validator targetValidator;
  public SpringValidatorAdapter(javax.validation.Validator targetValidator) {
    Assert.notNull(targetValidator, "Target Validator must not be null");
    this.targetValidator = targetValidator;
  }
  // 简单的说:默认支持校验所有的Bean类型~~~
  @Override
  public boolean supports(Class<?> clazz) {
    return (this.targetValidator != null);
  }
  // processConstraintViolations做的事一句话解释:
  // 把ConstraintViolations错误消息,全都适配放在Errors(BindingResult)里面存储着
  @Override
  public void validate(Object target, Errors errors) {
    if (this.targetValidator != null) {
      processConstraintViolations(this.targetValidator.validate(target), errors);
    }
  }
  @Override
  public void validate(Object target, Errors errors, Object... validationHints) {
    if (this.targetValidator != null) {
      processConstraintViolations(this.targetValidator.validate(target,  asValidationGroups(validationHints)), errors);
    }
  }
  @SuppressWarnings("unchecked")
  @Override
  public void validateValue(Class<?> targetType, String fieldName, @Nullable Object value, Errors errors, Object... validationHints) {
    if (this.targetValidator != null) {
      processConstraintViolations(this.targetValidator.validateValue(
          (Class) targetType, fieldName, value, asValidationGroups(validationHints)), errors);
    }
  }
  // 把validationHints都转换为group (支识别Class类型哦)
  private Class<?>[] asValidationGroups(Object... validationHints) {
    Set<Class<?>> groups = new LinkedHashSet<>(4);
    for (Object hint : validationHints) {
      if (hint instanceof Class) {
        groups.add((Class<?>) hint);
      }
    }
    return ClassUtils.toClassArray(groups);
  }
  // 关于Implementation of JSR-303 Validator interface  省略...
}


这个适配器它把所有的Spring接口的校验方法,最终都委托给了org.springframework.validation.Validator,这样就可以完美的和JSR结合起来使用了,功能更加的强大~

虽然本类它是个Class实体类,但是一般来说不建议直接使用它


相关文章
|
8天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
32 2
|
1月前
|
Java API 数据库
如何使用Spring Boot构建RESTful API,以在线图书管理系统为例
【10月更文挑战第9天】本文介绍了如何使用Spring Boot构建RESTful API,以在线图书管理系统为例,从项目搭建、实体类定义、数据访问层创建、业务逻辑处理到RESTful API的实现,详细展示了每个步骤。通过Spring Boot的简洁配置和强大功能,开发者可以高效地开发出功能完备、易于维护的Web应用。
60 3
|
1月前
|
IDE Java API
基于Spring Boot REST API设计指南
【10月更文挑战第4天】 在现代的软件开发中,RESTful API已经成为了构建网络应用的标准之一。它通过HTTP协议提供了与资源交互的方式,使得不同的应用程序能够进行数据交互。Spring Boot作为一个功能强大的框架,它简化了配置和开发流程,成为了构建RESTful API的理想选择。本文将详细介绍如何在Spring Boot中设计和实现高质量的RESTful API,并提供一些最佳实践。
49 1
|
1月前
|
缓存 Java API
基于Spring Boot REST API设计指南
【10月更文挑战第11天】 在构建现代Web应用程序时,RESTful API已成为一种标准,使得不同的应用程序能够通过HTTP协议进行通信,实现资源的创建、读取、更新和删除等操作。Spring Boot作为一个功能强大的框架,能够轻松创建RESTful API。本文将详细介绍如何在Spring Boot中设计和实现高质量的RESTful API。
122 61
|
15天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
70 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
1月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
83 1
|
1月前
|
安全 Java API
基于Spring Boot REST API设计指南
【10月更文挑战第10天】 在现代Web应用开发中,RESTful API扮演着至关重要的角色。Spring Boot作为一个高效、便捷的Java开发框架,为构建RESTful API提供了强大的支持。本文将分享基于Spring Boot的REST API设计指南,涵盖从项目初始化到API文档配置的全过程。
45 0
|
XML NoSQL Java
Spring核心(IoC) 入门解读
Spring核心(IoC) 入门解读
171 1
Spring核心(IoC) 入门解读

热门文章

最新文章