在上述的这篇文章里,介绍了我们常用的 Spring MVC 的对象参数校验,是怎么完成校验的过程的,这篇来介绍一个更深入的问题。因为上篇中介绍的过程,适用于对象中的属性都是常用的 Java 数据类型,比如基本数据类型或者字符串等,如果这个对象中包含一个其它类型的参数,会怎么样呢?
比如这样的类型:
@Data
public class User{
@Size(min = 6, max = 20)
private String username;
private Avatar avatar;
@Data
public static class Avatar{
@NotNull
private String imageUrl;
}
}
在 User 类型中,有一个 Avatar 类型的属性,Avatar 类型中有一个 String 类型的属性 imageUrl 需要校验非空。
在上一篇中,最后介绍了执行校验过程的 AbstractMessageConverterMethodProcessor 类中的 validateIfApplicable 方法,在这个方法中,最后对符合要求的参数对象执行校验的是:
binder.validate(validationHints);
其具体执行的代码,可以在 ValidatorImpl 类的 validate 方法中找到。
@Override
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
sanityCheckGroups( groups );
@SuppressWarnings("unchecked")
Class<T> rootBeanClass = (Class<T>) object.getClass();
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
if ( !rootBeanMetaData.hasConstraints() ) {
return Collections.emptySet();
}
BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidate( rootBeanClass, rootBeanMetaData, object );
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(
validatorScopedContext.getParameterNameProvider(),
object,
validationContext.getRootBeanMetaData(),
PathImpl.createRootPath()
);
return validateInContext( validationContext, valueContext, validationOrder );
}
在这里,会根据要校验的对象的类型,构建一个 BeanMetaData 类型的元信息,这个元信息中包含了需要做校验的字段和校验相关的信息。在构建 BeanMetaData 的过程中,会判断是否需要对成员字段进行级联校验,判断的方式就是这个字段是否被 @Valid
修饰(注意不是 @Validated
)。级联校验的过程,与宿主对象的校验过程大体相同。
因此,文章开头的写法,Spring 不会去校验 Avatar 中的 imageUrl 是否合法,如果需要让这个校验生效,则需要给 avatar 属性增加 @Valid
注解:
@Valid
private Avatar avatar;
这样才会对 avatar 中的 imageUrl 进行校验。
在 Spring MVC 的使用过程中,我们会发现很多非常符合直觉的功能特性,比如将请求参数绑定到 Controller 的方法参数,或者直接返回一个 Java 对象就可以完成 JSON 数据的转换等等。但往往我们会习惯这种「被照顾得很好」的开发方式,依靠直觉去判断很多功能特性的用法。
如果遇到类似的问题,运行结果不符合开发时的预期,最好的方式就是搞懂其中的原理,从原理中寻找最佳地解决方式。