一、核心架构概述
Spring Boot Validation 的核心是「JSR-380 规范 + Hibernate Validator 实现 + Spring 自动配置 + Spring MVC 集成」,其整体架构分为三层:
- 规范层(JSR-380): 定义校验注解(如 @NotNull)、校验器接口(ConstraintValidator)等标准 API,保证不同实现的通用性。
- 实现层(Hibernate Validator): JSR-380 规范的参考实现,提供默认校验注解的验证器、注解解析、校验执行等核心逻辑。
- 集成层(Spring): 通过 ValidationAutoConfiguration 完成自动配置,结合 Spring MVC 实现请求参数的自动校验,提供全局异常处理、AOP 方法校验等扩展能力。
其工作流程可概括为:请求进入→参数解析→校验判断→执行校验→异常抛出→统一处理。
二、自动配置:ValidationAutoConfiguration
Spring Boot 通过 ValidationAutoConfiguration 完成校验组件的自动配置,核心是创建两个关键 Bean,为后续校验提供基础能力:
1. 核心配置类源码解析
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ExecutableValidator.class) // 存在校验相关类时生效
@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")
@Import(PrimaryDefaultValidatorPostProcessor.class)
public class ValidationAutoConfiguration {
/**
* 核心校验器工厂:LocalValidatorFactoryBean
* 作用:创建 Validator 实例,整合 Hibernate Validator 实现
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(Validator.class) // 允许用户自定义 Validator
public static LocalValidatorFactoryBean defaultValidator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
// 消息插值器:用于解析注解中的 message(支持国际化)
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
/**
* 方法参数校验处理器:MethodValidationPostProcessor
* 作用:通过 AOP 动态代理拦截方法,支持基本类型/单个参数的校验
*/
@Bean
@ConditionalOnMissingBean
public static MethodValidationPostProcessor methodValidationPostProcessor(
Environment environment, @Lazy Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
// 配置 AOP 代理方式(默认使用 CGLIB 代理)
boolean proxyTargetClass = environment.getProperty(
"spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
processor.setValidator(validator); // 关联核心校验器
return processor;
}
}
2. 关键 Bean 详解
(1)LocalValidatorFactoryBean
- 本质:Spring 对 JSR-380 ValidatorFactory 的封装,是校验能力的核心来源。
- 核心功能:
- 加载底层校验实现(默认是 Hibernate Validator):通过 META-INF/services/javax.validation.spi.ValidationProvider 文件自动发现第三方校验实现。
- 创建 Validator 实例:Validator 是 JSR-380 规范定义的核心接口,提供对象校验、方法参数校验等方法。
- 整合消息插值器(MessageInterpolator):用于解析校验注解中的 message 属性,支持国际化消息。
(2)MethodValidationPostProcessor
- 本质:基于 Spring AOP 的方法参数校验处理器,解决 Spring MVC 只能校验对象参数的局限性。
- 核心功能:
- 对标注 @Validated 注解的类创建动态代理(CGLIB 或 JDK 代理)。
- 拦截代理类的方法调用,在方法执行前对参数进行校验(支持基本类型、单个参数校验)。
- 校验不通过时抛出 ConstraintViolationException,与对象参数校验的异常类型区分。
三、Spring MVC 中的参数校验流程
当请求进入 Spring MVC 控制器时,参数校验发生在「参数解析阶段」,核心由 ModelAttributeMethodProcessor(对象参数解析器)和 RequestResponseBodyMethodProcessor(JSON 参数解析器)完成,以下以 ModelAttributeMethodProcessor 为例剖析核心流程:
1. 核心流程拆解
步骤 1:参数解析与对象绑定
ModelAttributeMethodProcessor 的 resolveArgument 方法负责将请求参数(表单 / JSON)绑定到目标对象(如 TestRequest),生成 WebDataBinder 对象(数据绑定器)。
步骤 2:校验判断(validateIfApplicable)
通过 validateIfApplicable 方法判断当前参数是否需要校验,核心逻辑由 determineValidationHints 实现:
- 检查参数是否带有 @Valid、@Validated 注解,或自定义的以 Valid 开头的注解(如 @ValidUser)。
- 若存在上述注解,提取注解中的分组信息(如 AddGroup.class),准备执行校验。
步骤 3:执行校验(binder.validate ())
调用 WebDataBinder 的 validate 方法,内部通过 LocalValidatorFactoryBean 创建的 Validator 实例执行校验:
- 遍历目标对象的所有字段,查找带有校验注解的字段。
- 为每个注解匹配对应的验证器(如 @NotBlank 对应 NotBlankValidator)。
- 执行验证器的 isValid 方法,收集校验失败的错误信息。
步骤 4:异常抛出
若校验失败(bindingResult.hasErrors() == true),根据参数提交方式抛出不同异常:
- 表单提交(application/x-www-form-urlencoded):抛出 BindException。
- JSON 提交(application/json):抛出 MethodArgumentNotValidException(由 RequestResponseBodyMethodProcessor 处理)。
2. 关键源码片段解析
(1)参数解析与校验触发(ModelAttributeMethodProcessor)
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver {
@Override
public final Object resolveArgument(MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) throws Exception {
// 1. 构建目标对象(如 TestRequest)
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = mavContainer.containsAttribute(name) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest);
// 2. 绑定请求参数到目标对象(如将 req 参数值注入 TestRequest 的 req 字段)
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, webRequest);
// 3. 执行参数校验(核心步骤)
validateIfApplicable(binder, parameter);
// 4. 校验失败则抛出异常
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// 5. 将绑定结果存入 ModelAndViewContainer
mavContainer.addAttribute(name, binder.getTarget());
return binder.getTarget();
}
/**
* 判断是否需要执行校验:参数带有 @Valid/@Validated 或自定义 Valid 开头的注解
*/
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
Object[] validationHints = determineValidationHints(ann);
if (validationHints != null) {
binder.validate(validationHints);
break;
}
}
}
/**
* 提取校验注解的提示信息(支持分组校验)
*/
@Nullable
private Object[] determineValidationHints(Annotation ann) {
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
return (hints == null ? new Object[0] : (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}));
}
return null;
}
}
(2)校验执行(WebDataBinder.validate ())
public class WebDataBinder implements PropertyEditorRegistry, TypeConverter {
public void validate(Object... validationHints) {
if (this.target != null) {
// 获取 Validator 实例(即 LocalValidatorFactoryBean 创建的实例)
Validator validator = getValidator();
if (validator != null) {
// 执行校验,将错误信息存入 BindingResult
BindingResult bindingResult = getBindingResult();
Set<ConstraintViolation<Object>> violations = validator.validate(this.target, validationHints);
for (ConstraintViolation<Object> violation : violations) {
String field = violation.getPropertyPath().toString();
Object value = violation.getInvalidValue();
String message = violation.getMessage();
bindingResult.addError(new FieldError(
getObjectName(), field, value, false, null, null, message));
}
}
}
}
}
四、底层实现:Hibernate Validator 核心逻辑
Spring Boot Validation 的底层校验能力依赖 Hibernate Validator(JSR-380 规范的参考实现),其核心逻辑是「注解解析→验证器匹配→校验执行」,关键组件包括 ConstraintHelper、ConstraintValidator、ValidatorFactory 等。
1. 核心组件详解
(1)ConstraintHelper:约束助手类
- 核心作用: 管理所有校验注解与验证器的映射关系,提供验证器查找、注解有效性判断等功能。
- 关键逻辑:
- 初始化默认校验注解与验证器:在构造方法中注册所有 JSR-380 标准注解(如 @NotBlank)对应的默认验证器(如 NotBlankValidator)。
- 验证器缓存:将默认验证器存储在 builtinConstraints 集合中,避免重复创建。
- 自定义注解校验:判断自定义注解是否标注 @Constraint 注解,且包含 message、groups、payload 三个必填属性。
(2)ConstraintValidator:校验器接口
- 本质: JSR-380 规范定义的校验器接口,所有校验注解的验证逻辑都通过实现该接口完成。
- 核心方法:
- initialize(A constraintAnnotation):初始化方法,可获取注解的属性值(如 @Size 的 min 和 max)。
- isValid(T value, ConstraintValidatorContext context):校验核心方法,返回 true 表示校验通过,false 表示失败。
(3)ValidatorFactory 与 Validator
- ValidatorFactory: 校验器工厂,负责创建 Validator 实例,整合注解解析、消息插值、验证器查找等功能。
- Validator: 校验执行入口,提供 validate(对象校验)、validateParameters(方法参数校验)等方法。
2. 关键源码解析(ConstraintHelper)
public class ConstraintHelper {
// 存储默认校验注解与验证器的映射关系
private final Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> builtinConstraints = new HashMap<>();
public ConstraintHelper() {
// 初始化默认校验注解与验证器(以 @NotBlank 为例)
putConstraint(builtinConstraints, NotBlank.class, NotBlankValidator.class);
putConstraint(builtinConstraints, NotNull.class, NotNullValidator.class);
putConstraint(builtinConstraints, Email.class, EmailValidator.class);
// 其他默认注解初始化...
}
/**
* 判断是否为有效的校验注解:
* 1. 是内置注解(如 @NotBlank);
* 2. 自定义注解且标注了 @Constraint 注解
*/
public boolean isConstraintAnnotation(Class<? extends Annotation> annotationType) {
if (isBuiltinConstraint(annotationType)) {
return true;
}
if (annotationType.getAnnotation(Constraint.class) == null) {
return false;
}
// 校验自定义注解的必填参数(message、groups、payload)
return externalConstraints.computeIfAbsent(annotationType, a -> {
assertMessageParameterExists(a);
assertGroupsParameterExists(a);
assertPayloadParameterExists(a);
return Boolean.TRUE;
});
}
/**
* 获取校验器:
* 1. 优先从内置映射中查找;
* 2. 未找到则从 @Constraint 注解的 validatedBy 属性获取(自定义校验器)
*/
private <A extends Annotation> List<ConstraintValidatorDescriptor<A>> getDefaultValidatorDescriptors(Class<A> annotationType) {
List<ConstraintValidatorDescriptor<A>> builtInValidators = (List<ConstraintValidatorDescriptor<A>>) builtinConstraints.get(annotationType);
if (builtInValidators != null) {
return builtInValidators;
}
// 从自定义注解的 @Constraint(validatedBy = XXX.class) 中获取验证器
Class<? extends ConstraintValidator<A, ?>>[] validatedBy =
(Class<? extends ConstraintValidator<A, ?>>[]) annotationType.getAnnotation(Constraint.class).validatedBy();
return Stream.of(validatedBy)
.map(c -> ConstraintValidatorDescriptor.forClass(c, annotationType))
.collect(Collectors.toList());
}
}
3. 校验执行流程(Hibernate Validator)
以 @NotBlank 注解校验为例,完整执行流程如下:
- 调用 Validator.validate(target) 方法,传入待校验对象。
- 遍历对象的所有字段,通过 ConstraintHelper 判断字段是否带有有效校验注解(如 @NotBlank)。
- 通过 ConstraintHelper.getDefaultValidatorDescriptors 找到 @NotBlank 对应的验证器 NotBlankValidator。
- 调用 NotBlankValidator.initialize(NotBlank annotation) 初始化(此处无特殊逻辑)。
- 调用 NotBlankValidator.isValid(String value, ConstraintValidatorContext context):
- 判断 value 是否为 null,若是则返回 false。
- 去除 value 首尾空格,判断长度是否 > 0,若是则返回 true,否则返回 false。
- 若校验失败,封装 ConstraintViolation 对象(包含错误字段、错误信息、无效值等),最终抛给上层框架。
五、方法参数校验(非对象类型)原理
Spring MVC 原生仅支持对象参数校验,对于基本类型(如 String、Integer)或单个参数的校验,需通过 MethodValidationPostProcessor 实现,其原理如下:
1. 核心原理:AOP 动态代理
- MethodValidationPostProcessor 实现了 BeanPostProcessor 接口,会在 Spring 容器初始化标注 @Validated 注解的 Bean 时,为其创建动态代理(CGLIB 或 JDK 代理)。
- 代理类会拦截目标方法的调用,在方法执行前通过 Validator 实例对方法参数进行校验。
2. 执行流程
- Controller 类上添加 @Validated 注解,标记需要进行方法参数校验。
- 方法参数上添加校验注解(如 @NotBlank、@Min)。
- 请求进入时,代理类拦截方法调用,提取方法参数及对应的校验注解。
- 调用 Validator.validateParameters 方法执行校验。
- 校验不通过时抛出 ConstraintViolationException,由全局异常处理器捕获处理。
3. 关键源码片段(MethodValidationInterceptor)
MethodValidationPostProcessor 内部通过 MethodValidationInterceptor 实现拦截逻辑:
public class MethodValidationInterceptor implements MethodInterceptor {
private final Validator validator;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 1. 获取目标方法、参数、目标对象
Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments();
Object target = invocation.getThis();
// 2. 执行方法参数校验
Set<ConstraintViolation<Object>> violations = validator.validateParameters(
target, method, arguments, getValidationGroups(invocation));
// 3. 校验失败则抛出异常
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
// 4. 校验通过,执行目标方法
Object result = invocation.proceed();
// 5. (可选)执行方法返回值校验
Set<ConstraintViolation<Object>> returnViolations = validator.validateReturnValue(
target, method, result, getValidationGroups(invocation));
if (!returnViolations.isEmpty()) {
throw new ConstraintViolationException(returnViolations);
}
return result;
}
}
六、总结
Spring Boot Validation 的底层原理可概括为:
- 规范驱动: 基于 JSR-380 规范定义的标准 API,确保校验逻辑的通用性和扩展性。
- 自动配置: 通过 ValidationAutoConfiguration 自动创建 LocalValidatorFactoryBean 和 MethodValidationPostProcessor,简化配置。
- Spring MVC 集成: 在参数解析阶段触发校验,通过异常机制向上层传递校验结果。
- Hibernate Validator 实现: 提供默认校验逻辑和注解解析能力,支持自定义扩展。
理解其底层原理后,可更灵活地使用校验功能(如分组校验、嵌套校验),也能快速定位校验相关的问题(如注解不生效、异常未捕获等),同时为自定义校验注解提供理论支撑。