一、引言
问题:在代码编写的过程中,数据值的校验在JavaEE三层架构(展示层、业务层、数据访问层)均有涉及,各层的校验需求又是不尽相同的,因此往往会伴随着代码冗余,重复的校验逻辑出现在三层代码中。
简介:JSR-303 Bean-Validator伴随着此类问题应运而生,Hibernate-Validator就是一个遵循JSR-303规范的优秀实践。
图片截取自Hibernate Validator 官方pdf文档。
Hibernate-Validator提供了较为完善、便捷的校验方式解决了代码校验问题。 常用于提供字段的校验,比如字段非空、字段长度限制、邮箱验证等等。
二、测试实例
下面引用Hibernate-Validator源码中ValidatorTest测试类下的方法为例:
class A {
@NotNull
String b;
@Valid
C c;
@Valid
D d;
}
@GroupSequence({ TestGroup.class, C.class })
class C {
@Pattern(regexp = "[0-9]+", groups = TestGroup.class)
@Length(min = 2)
String id;
C(String id) {
this.id = id;
}
}
@Test
public void testValidate() {
ValidatorFactory factory = configuration.buildValidatorFactory();
Validator validator = factory.getValidator();
A testInstance = new A();
testInstance.c = new C( "aaa" );
Set<ConstraintViolation<SerializableClass>> constraintViolations = validator.validate( testInstance );
Set<ConstraintViolation<A>> constraintViolations = validator.validateProperty( testInstance, "c.id" );
Set<ConstraintViolation<A>> constraintViolations = validator.validateValue( A.class, "c.id", "aaa" );
}
由上例可以看出,Hibernate-Validator主要是将JSR-303内置的约束以及Hibernate-Validator扩展的自定义约束通过注解的形式添加到属性上,来实现对对象内属性的校验功能。下面我们将通过上述例子拆分及解析校验实现的流程及细节,本文使用Hibernate-Validator下的7.0.1.Final版本解释源码。
三. Hibernate-Validator具体实现
1. 获取Validator实例
想要对对象进行校验,首先需要获得一个Validator校验器实例,由上述代码可知
ValidatorFactory factory = configuration.buildValidatorFactory();
Validator validator = factory.getValidator();
校验器获取方法在代码实现上较为简单,主要是遵循Java内部的SPI机制实现Java提供的相关接口实现。
主要实现分为两步:
a. validatorFactory 工厂创建
public static ValidatorFactory buildDefaultValidatorFactory() {
return byDefaultProvider().configure().buildValidatorFactory();
}
// 构建相关配置文件
public Configuration<?> configure() {
// ValidationProviderResolver 的主要作用是确定环境下可用的 ValidationProvider 列表
// 其唯一实现是 jakarta.validation.Validation 下的内部类 DefaultValidationProviderResolver
ValidationProviderResolver resolver = this.resolver == null ? this.getDefaultValidationProviderResolver() : this.resolver;
List validationProviders;
try {
// ValidationProvider的作用是提供程序校验器,通过其下的buildValidatorFactory方法构建ValidatorFactory工厂
// Hibernate-Validator对其提供了唯一的实现类HibernateValidator构建并返回工厂实现ValidatorFactoryImpl
validationProviders = resolver.getValidationProviders();
} catch (ValidationException var6) {
throw var6;
} catch (RuntimeException var7) {
throw new ValidationException("Unable to get available provider resolvers.", var7);
}
if (validationProviders.isEmpty()) {
String msg = "Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.";
throw new NoProviderFoundException(msg);
} else {
try {
// 从 ValidationProvider 中获取相关通用配置
Configuration<?> config = ((ValidationProvider)resolver.getValidationProviders().get(0)).createGenericConfiguration(this);
return config;
} catch (RuntimeException var5) {
throw new ValidationException("Unable to instantiate Configuration.", var5);
}
}
}
// 根据配置文件构建用于创建Validator的工厂并初始化相关参数
public final ValidatorFactory buildValidatorFactory() {
loadValueExtractorsFromServiceLoader();
// 解析Validation.xml,如何存在则解析其配置
parseValidationXml();
for ( ValueExtractorDescriptor valueExtractorDescriptor : valueExtractorDescriptors.values() ) {
validationBootstrapParameters.addValueExtractorDescriptor( valueExtractorDescriptor );
}
ValidatorFactory factory = null;
try {
// 判断是否指定了Validator,指定了Validator则直接返回Factory
if ( isSpecificProvider() ) {
factory = validationBootstrapParameters.getProvider().buildValidatorFactory( this );
}
else {
// 判断是否指定了 ValidatorProvider
final Class<? extends ValidationProvider<?>> providerClass = validationBootstrapParameters.getProviderClass();
if ( providerClass != null ) {
for ( ValidationProvider<?> provider : providerResolver.getValidationProviders() ) {
if ( providerClass.isAssignableFrom( provider.getClass() ) ) {
factory = provider.buildValidatorFactory( this );
break;
}
}
if ( factory == null ) {
throw LOG.getUnableToFindProviderException( providerClass );
}
}
else {
List<ValidationProvider<?>> providers = providerResolver.getValidationProviders();
assert providers.size() != 0; // I run therefore I am
// 构建 ValidatorFactory 时初始化了很多对象,包括方法校验配置、分组排序、注解到注解校验实现的映射等
factory = providers.get( 0 ).buildValidatorFactory( this );
}
}
}
finally {
// close all input streams opened by this configuration
for ( InputStream in : configurationStreams ) {
try {
in.close();
}
catch (IOException io) {
LOG.unableToCloseInputStream();
}
}
}
return factory;
}
b. 获取Validator实例
@Override
// getValidator 是 ValidatorFactoryImpl 下最重要的方法,用于获取 Validator 实例
public Validator getValidator() {
return createValidator(
constraintCreationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory(),
constraintCreationContext,
validatorFactoryScopedContext,
methodValidationConfiguration
);
}
2. Validator校验
由实例中列举的validator校验方法可知,Validator校验分为三种类型,分别是:
- validate:针对所有属性进行校验;
- validateProperty:针对某一个具体属性进行校验;
- validateValue:针对某一个具体属性及特定值进行校验。
本文选用validate的相关校验流程及源码进行解析,源码功能使用代码注释的方式解释。
validate具体校验流程如下所示:
- 执行ValidatorImpl下的validate方法,封装validationContext(校验器上下文)、valueContext(校验对象上下文)及validationOrder(校验群组及排序信息)数据内容,方便后续调用。
// 校验对象下的所有约束是否正常
@Override
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
// 校验对象是否为空
Contracts.assertNotNull(object, MESSAGES.validatedObjectMustNotBeNull());
// 校验Class Group是否为空
sanityCheckGroups(groups);
@SuppressWarnings("unchecked")
// 获取对象的Class
Class<T> rootBeanClass = (Class<T>) object.getClass();
// 通过反射获取对象内所有Field、Method、Class相关的元数据信息
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData(rootBeanClass);
// 判断是否含有约束,无约束则直接返回
if (!rootBeanMetaData.hasConstraints()) {
return Collections.emptySet();
}
// 将对象元数据和实际对象内容封装成一个完成的ValidationContext
BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidate(rootBeanClass,
rootBeanMetaData, object);
// 接口分组排序,分组排序决定了哪个Group先校验,哪个Group后校验,用于处理有先后关系的校验
ValidationOrder validationOrder = determineGroupValidationOrder(groups);
// 填充校验对象上下文信息
BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(
validatorScopedContext.getParameterNameProvider(), object, validationContext.getRootBeanMetaData(),
PathImpl.createRootPath());
// 根据校验文件必要信息进行校验操作
return validateInContext(validationContext, valueContext, validationOrder);
}
- 执行ValidatorImpl下的validateInContext方法,根据传入对象的群组、级联信息分别进行校验
private <T, U> Set<ConstraintViolation<T>> validateInContext(BaseBeanValidationContext<T> validationContext,
BeanValueContext<U, Object> valueContext, ValidationOrder validationOrder) {
// 如果Bean为null则直接返回空
if (valueContext.getCurrentBean() == null) {
return Collections.emptySet();
}
// 取出当前Bean元数据
BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
if (beanMetaData.isDefaultGroupSequenceRedefined()) {
validationOrder.assertDefaultGroupSequenceIsExpandable(
beanMetaData.getDefaultGroupSequence(valueContext.getCurrentBean()));
}
// 校验当前对象内的约束内容
Iterator<Group> groupIterator = validationOrder.getGroupIterator();
while (groupIterator.hasNext()) {
Group group = groupIterator.next();
valueContext.setCurrentGroup(group.getDefiningClass());
// 校验当前对象
validateConstraintsForCurrentGroup(validationContext, valueContext);
// 判断约束校验是否失败
if (shouldFailFast(validationContext)) {
return validationContext.getFailingConstraints();
}
}
// 级联校验,即校验一个对象内可能存在的需要校验的其他对象
groupIterator = validationOrder.getGroupIterator();
while (groupIterator.hasNext()) {
Group group = groupIterator.next();
valueContext.setCurrentGroup(group.getDefiningClass());
// 校验级联对象
validateCascadedConstraints(validationContext, valueContext);
// 判断约束校验是否失败
if (shouldFailFast(validationContext)) {
return validationContext.getFailingConstraints();
}
}
// 如果需要校验的对象包含GroupSequece,按次序校验则走以下方法
Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
while (sequenceIterator.hasNext()) {
Sequence sequence = sequenceIterator.next();
for (GroupWithInheritance groupOfGroups : sequence) {
int numberOfViolations = validationContext.getFailingConstraints().size();
for (Group group : groupOfGroups) {
valueContext.setCurrentGroup(group.getDefiningClass());
// 校验对象
validateConstraintsForCurrentGroup(validationContext, valueContext);
// 判断约束校验是否失败
if (shouldFailFast(validationContext)) {
return validationContext.getFailingConstraints();
}
// 校验级联对象
validateCascadedConstraints(validationContext, valueContext);
// 判断约束校验是否失败
if (shouldFailFast(validationContext)) {
return validationContext.getFailingConstraints();
}
}
if (validationContext.getFailingConstraints().size() > numberOfViolations) {
break;
}
}
}
return validationContext.getFailingConstraints();
}
- 根据校验属性的Group执行相应校验方法
private void validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext,
BeanValueContext<?, Object> valueContext) {
if (!valueContext.validatingDefault()) {
// 如果校验的group非Default Group,走该校验方法
validateConstraintsForNonDefaultGroup(validationContext, valueContext);
}
else {
// 如果校验的group为Default Group,走该校验方法
validateConstraintsForDefaultGroup(validationContext, valueContext);
}
}
- 解析当前对象的继承结构,根据继承结构和默认的GroupSequence进行校验
// 默认约束校验
private <U> void validateConstraintsForDefaultGroup(BaseBeanValidationContext<?> validationContext,
BeanValueContext<U, Object> valueContext) {
// 获取currentBeanMetaData对象校验当前对象内容
final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();
// 评估类的继承结构中每个类的约束,依次校验该类及其父类约束
for (Class<? super U> clazz : beanMetaData.getClassHierarchy()) {
BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData(clazz);
boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();
// 如果默认的GroupSequence被重新定义,说明需要对属性校验排序处理,则走以下方法进行校验
if (defaultGroupSequenceIsRedefined) {
Iterator<Sequence> defaultGroupSequence = hostingBeanMetaData
.getDefaultValidationSequence(valueContext.getCurrentBean());
Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getMetaConstraints();
while (defaultGroupSequence.hasNext()) {
// 区分GroupSequence进行校验
for (GroupWithInheritance groupOfGroups : defaultGroupSequence.next()) {
boolean validationSuccessful = true;
for (Group defaultSequenceMember : groupOfGroups) {
// 遍历GroupSequence下的Group进行校验
validationSuccessful = validateConstraintsForSingleDefaultGroupElement(validationContext,
valueContext, validatedInterfaces, clazz, metaConstraints, defaultSequenceMember)
&& validationSuccessful;
}
validationContext.markCurrentBeanAsProcessed(valueContext);
if (!validationSuccessful) {
break;
}
}
}
}
// fast path in case the default group sequence hasn't been redefined
else {
// 查询当前Bean下可以直接进行校验的属性
Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints();
// 执行校验
validateConstraintsForSingleDefaultGroupElement(validationContext, valueContext, validatedInterfaces,
clazz, metaConstraints, Group.DEFAULT_GROUP);
validationContext.markCurrentBeanAsProcessed(valueContext);
}
// 层级中的所有约束均完成校验,则停止校验
if (defaultGroupSequenceIsRedefined) {
break;
}
}
}
- 循环校验对象的每个属性
private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanValidationContext<?> validationContext,
ValueContext<U, Object> valueContext, final Map<Class<?>, Class<?>> validatedInterfaces, Class<? super U> clazz,
Set<MetaConstraint<?>> metaConstraints, Group defaultSequenceMember) {
boolean validationSuccessful = true;
valueContext.setCurrentGroup(defaultSequenceMember.getDefiningClass());
// 循环校验对象的每个属性
for (MetaConstraint<?> metaConstraint : metaConstraints) {
// HV-466, an interface implemented more than one time in the hierarchy has to be validated only one
// time. An interface can define more than one constraint, we have to check the class we are validating.
final Class<?> declaringClass = metaConstraint.getLocation().getDeclaringClass();
if (declaringClass.isInterface()) {
Class<?> validatedForClass = validatedInterfaces.get(declaringClass);
if (validatedForClass != null && !validatedForClass.equals(clazz)) {
continue;
}
validatedInterfaces.put(declaringClass, clazz);
}
// 校验类非interface接口则正常校验
boolean tmp = validateMetaConstraint(validationContext, valueContext, valueContext.getCurrentBean(),
metaConstraint);
// 快速失败
if (shouldFailFast(validationContext)) {
return false;
}
validationSuccessful = validationSuccessful && tmp;
}
return validationSuccessful;
}
- 判断对象是否满足校验条件并对对象进行校验
private boolean validateMetaConstraint(BaseBeanValidationContext<?> validationContext,
ValueContext<?, Object> valueContext, Object parent, MetaConstraint<?> metaConstraint) {
BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
valueContext.appendNode(metaConstraint.getLocation());
boolean success = true;
// 判断待校验文件是否满足校验条件,如对象属性分组数及校验数是否匹配,校验分组是否与当前分组匹配等
if (isValidationRequired(validationContext, valueContext, metaConstraint)) {
if (parent != null) {
valueContext.setCurrentValidatedValue(valueContext.getValue(parent, metaConstraint.getLocation()));
}
// 对象属性校验
success = metaConstraint.validateConstraint(validationContext, valueContext);
validationContext.markConstraintProcessed(valueContext.getCurrentBean(), valueContext.getPropertyPath(),
metaConstraint);
}
// 对象属性校验完成后重置校验数据
valueContext.resetValueState(originalValueState);
return success;
}
- 具体对象值提取校验逻辑
public boolean validateConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext) {
boolean success = true;
// 提取集合内容器循环校验
if (valueExtractionPath != null) {
Object valueToValidate = valueContext.getCurrentValidatedValue();
if (valueToValidate != null) {
TypeParameterValueReceiver receiver = new TypeParameterValueReceiver(validationContext, valueContext,
valueExtractionPath);
ValueExtractorHelper.extractValues(valueExtractionPath.getValueExtractorDescriptor(), valueToValidate,
receiver);
success = receiver.isSuccess();
}
}
// 常规校验流程
else {
success = doValidateConstraint(validationContext, valueContext);
}
return success;
}
- 失败异常处理
public final boolean validateConstraints(ValidationContext<?> validationContext, ValueContext<?, ?> valueContext) {
List<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts = new ArrayList<>(5);
// 属性值执行校验
validateConstraints(validationContext, valueContext, violatedConstraintValidatorContexts);
// 如果violatedConstraintValidatorContexts集合不为空,则说明校验失败,校验失败属性处理
if (!violatedConstraintValidatorContexts.isEmpty()) {
for (ConstraintValidatorContextImpl constraintValidatorContext : violatedConstraintValidatorContexts) {
for (ConstraintViolationCreationContext constraintViolationCreationContext : constraintValidatorContext
.getConstraintViolationCreationContexts()) {
validationContext.addConstraintFailure(valueContext, constraintViolationCreationContext,
constraintValidatorContext.getConstraintDescriptor());
}
}
return false;
}
return true;
}
- 获取属性校验对应校验器及构建约束校验器上下文
protected void validateConstraints(ValidationContext<?> validationContext, ValueContext<?, ?> valueContext,
Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {
if (LOG.isTraceEnabled()) {
LOG.tracef("Validating value %s against constraint defined by %s.", valueContext.getCurrentValidatedValue(),
descriptor);
}
// 获取对象相关的Validator校验器,注解与校验器的映射在ValidatorImpl初始化时就已经取出,存在校验上下文对象中。
// 通过AbstractConstraintValidatorManagerImpl下的findMatchingValidatorDescriptor查询并匹配到对应属性。
ConstraintValidator<B, ?> validator = getInitializedConstraintValidator(validationContext, valueContext);
// 构建约束校验器上下文
ConstraintValidatorContextImpl constraintValidatorContext = validationContext
.createConstraintValidatorContextFor(descriptor, valueContext.getPropertyPath());
// 校验,如果校验失败则加入集合violatedConstraintValidatorContexts
if (validateSingleConstraint(valueContext, constraintValidatorContext, validator).isPresent()) {
violatedConstraintValidatorContexts.add(constraintValidatorContext);
}
}
- 通过筛选出的validator完成校验
protected final <V> Optional<ConstraintValidatorContextImpl> validateSingleConstraint(
ValueContext<?, ?> valueContext, ConstraintValidatorContextImpl constraintValidatorContext,
ConstraintValidator<A, V> validator) {
boolean isValid;
try {
// 获取当前要校验的对象值value
@SuppressWarnings("unchecked")
V validatedValue = (V) valueContext.getCurrentValidatedValue();
// 使用注解和属性类型对应的Validator进行校验
isValid = validator.isValid(validatedValue, constraintValidatorContext);
}
catch (RuntimeException e) {
if (e instanceof ConstraintDeclarationException) {
throw e;
}
throw LOG.getExceptionDuringIsValidCallException(e);
}
if (!isValid) {
// We do not add these violations yet, since we don't know how they are
// going to influence the final boolean evaluation
return Optional.of(constraintValidatorContext);
}
return Optional.empty();
}
上述代码即是从ValidatorImpl下的validator方法开始到实现注解属性校验的基础校验过程源码解析。
validateProperty和validateValue两种方法与validate方法差异不大,此处不进行过多赘述。
当校验对象中存在嵌套对象需要校验时,此时需要使用@Valid注解标注出需要校验的对象,通过级联校验功能进行校验。
private <T, U> Set<ConstraintViolation<T>> validateInContext(BaseBeanValidationContext<T> validationContext,
BeanValueContext<U, Object> valueContext, ValidationOrder validationOrder) {
// 级联校验,即校验一个对象内可能存在的需要校验的其他对象
groupIterator = validationOrder.getGroupIterator();
while (groupIterator.hasNext()) {
Group group = groupIterator.next();
valueContext.setCurrentGroup(group.getDefiningClass());
// 校验级联对象
validateCascadedConstraints(validationContext, valueContext);
// 判断约束校验是否失败
if (shouldFailFast(validationContext)) {
return validationContext.getFailingConstraints();
}
}
return validationContext.getFailingConstraints();
}
private void validateCascadedConstraints(BaseBeanValidationContext<?> validationContext,
ValueContext<?, Object> valueContext) {
// 获取当前对象下的可校验内容
Validatable validatable = valueContext.getCurrentValidatable();
BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
// 校验当前对象下注释了@Valid的属性或方法参数
for (Cascadable cascadable : validatable.getCascadables()) {
valueContext.appendNode(cascadable);
// 确认级联校验对象处于可校验状态
if (isCascadeRequired(validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(),
cascadable.getConstraintLocationKind())) {
Object value = getCascadableValue(validationContext, valueContext.getCurrentBean(), cascadable);
CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData();
if (value != null) {
CascadingMetaData effectiveCascadingMetaData = cascadingMetaData
.addRuntimeContainerSupport(valueExtractorManager, value.getClass());
// 校验被@Valid注释的级联属性,递归validateInContext方法实现递归级联校验
if (effectiveCascadingMetaData.isCascading()) {
validateCascadedAnnotatedObjectForCurrentGroup(value, validationContext, valueContext,
effectiveCascadingMetaData);
}
// Container集合数据特殊处理
if (effectiveCascadingMetaData.isContainer()) {
ContainerCascadingMetaData containerCascadingMetaData = effectiveCascadingMetaData
.as(ContainerCascadingMetaData.class);
if (containerCascadingMetaData.hasContainerElementsMarkedForCascading()) {
// 校验Container内元素约束
validateCascadedContainerElementsForCurrentGroup(value, validationContext, valueContext,
containerCascadingMetaData.getContainerElementTypesCascadingMetaData());
}
}
}
}
// 重置valueContext值内容
valueContext.resetValueState(originalValueState);
}
}
private void validateCascadedAnnotatedObjectForCurrentGroup(Object value,
BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext,
CascadingMetaData cascadingMetaData) {
Class<?> originalGroup = valueContext.getCurrentGroup();
Class<?> currentGroup = cascadingMetaData.convertGroup(originalGroup);
// 判断数据是否已经校验过或者校验是否已经失败
if (validationContext.isBeanAlreadyValidated(value, currentGroup, valueContext.getPropertyPath())
|| shouldFailFast(validationContext)) {
return;
}
ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder(currentGroup,
currentGroup != originalGroup);
BeanValueContext<?, Object> cascadedValueContext = buildNewLocalExecutionContext(valueContext, value);
// 递归校验
validateInContext(validationContext, cascadedValueContext, validationOrder);
}
3. Validator功能拓展
Validator除基础的校验流程外,为了强化功能以满足更多场景的需要,校验流程中还提供了诸如Group(群组)、Sequence(优先级)等功能,下面将解读这些功能在校验中的相关类源码。
- Group(群组)
a. 为类中的属性约束定义群组
@Data
public class Driver extends Person {
@Min(value = 18, message = "You have to be 18 to drive a car", groups = DriverChecks.class)
public int age;
public Driver(String name) {
super(name);
}
}
b. 封装校验群组及校验顺序信息到ValidationOrder对象中
public ValidationOrder getValidationOrder(Collection<Class<?>> groups) {
// 如果Group为空则抛出异常
if ( groups == null || groups.size() == 0 ) {
throw LOG.getAtLeastOneGroupHasToBeSpecifiedException();
}
// Group集合下仅包含默认Group则直接返回
if ( groups.size() == 1 && groups.contains( Default.class ) ) {
return ValidationOrder.DEFAULT_GROUP;
}
// 所有的group类均为接口类,如果存在非接口Group则抛出异常
for ( Class<?> clazz : groups ) {
if ( !clazz.isInterface() ) {
throw LOG.getGroupHasToBeAnInterfaceException( clazz );
}
}
// 根据集合groups填充validationOrder群组及排序信息
DefaultValidationOrder validationOrder = new DefaultValidationOrder();
for ( Class<?> clazz : groups ) {
if ( Default.class.equals( clazz ) ) {
validationOrder.insertGroup( Group.DEFAULT_GROUP );
}
else if ( isGroupSequence( clazz ) ) {
insertSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), true, validationOrder );
}
else {
Group group = new Group( clazz );
validationOrder.insertGroup( group );
insertInheritedGroups( clazz, validationOrder );
}
}
return validationOrder;
}
c. validateInContext方法通过遍历ValidationOrder对象中的分组信息和序列信息进行校验
// 校验当前对象内的约束内容
Iterator<Group> groupIterator = validationOrder.getGroupIterator();
while (groupIterator.hasNext()) {
…………
}
// 如果需要校验的对象包含GroupSequece,按次序校验则走以下方法
Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
while (sequenceIterator.hasNext()) {
…………
}
d. 后续指定Group后直接执行循环校验,校验分组与指定分组相同的属性。
private boolean isValidationRequired(BaseBeanValidationContext<?> validationContext,
ValueContext<?, ?> valueContext, MetaConstraint<?> metaConstraint) {
if (!validationContext.appliesTo(metaConstraint)) {
return false;
}
if (validationContext.hasMetaConstraintBeenProcessed(valueContext.getCurrentBean(),
valueContext.getPropertyPath(), metaConstraint)) {
return false;
}
// 分组校验,如果需要校验的属性分组与当前正在校验分组不同,则不进行校验
if (!metaConstraint.getGroupList().contains(valueContext.getCurrentGroup())) {
return false;
}
return isReachable(validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(),
metaConstraint.getConstraintLocationKind());
}
分组校验通过对Validator注解增加分组的方式,限定了校验需要生效的范围,在调用validator方法时指定本次校验的分组,即可指定哪些方法需要校验。
- Sequence(序列)
用户可以通过GroupSequence的方式指定分组的优先级,高优先级的分组可以优先执行校验逻辑。
private void insertSequence(Class<?> sequenceClass, Class<?>[] sequenceElements, boolean cache, DefaultValidationOrder validationOrder) {
Sequence sequence = cache ? resolvedSequences.get( sequenceClass ) : null;
if ( sequence == null ) {
// 解析校验对象的GroupSequence校验优先级数据
sequence = resolveSequence( sequenceClass, sequenceElements, new ArrayList<Class<?>>() );
// 如果存在分组继承关系,则检查分组继承关系新增Group
sequence.expandInheritedGroups();
// 缓存校验优先级信息
if ( cache ) {
final Sequence cachedResolvedSequence = resolvedSequences.putIfAbsent( sequenceClass, sequence );
if ( cachedResolvedSequence != null ) {
sequence = cachedResolvedSequence;
}
}
}
validationOrder.insertSequence( sequence );
}
4. 自定义约束
除了Hibernate-Validator提供的约束外,用户在开发过程中经常需要基于自身业务需求自定义约束。
基于Hibernate-Validator框架拓展validator约束功能较为简单,仅需实现其自定义约束的两个部分,注解及其注解的约束实现。
注解是否为约束注解的解析实现在BeanMetaData对象的构造过程中,具体实现在org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider类下的findConstraintAnnotations方法中:
protected <A extends Annotation> List<ConstraintDescriptorImpl<?>> findConstraintAnnotations(Member member,
A annotation,
ElementType type) {
if ( annotation.annotationType().getPackage().getName().equals( "jdk.internal" ) ) {
return Collections.emptyList();
}
List<ConstraintDescriptorImpl<?>> constraintDescriptors = newArrayList();
List<Annotation> constraints = newArrayList();
Class<? extends Annotation> annotationType = annotation.annotationType();
if ( constraintHelper.isConstraintAnnotation( annotationType ) ) {
constraints.add( annotation );
}
else if ( constraintHelper.isMultiValueConstraint( annotationType ) ) {
constraints.addAll( constraintHelper.getConstraintsFromMultiValueConstraint( annotation ) );
}
for ( Annotation constraint : constraints ) {
final ConstraintDescriptorImpl<?> constraintDescriptor = buildConstraintDescriptor(
member, constraint, type
);
constraintDescriptors.add( constraintDescriptor );
}
return constraintDescriptors;
}
保证注解满足constraintHelper.isConstraintAnnotation方法中的要求即可,包括其中必要参数及注解的校验。
public boolean isConstraintAnnotation(Class<? extends Annotation> annotationType) {
// 查询是否是Hibernate-Validator自身约束,是则直接返回true
if (isBuiltinConstraint(annotationType)) {
return true;
}
// 自定义约束检查注解及参数是否合法
if (annotationType.getAnnotation(Constraint.class) == null) {
return false;
}
return externalConstraints.computeIfAbsent(annotationType, a -> {
assertMessageParameterExists(a);
assertGroupsParameterExists(a);
assertPayloadParameterExists(a);
assertValidationAppliesToParameterSetUpCorrectly(a);
assertNoParameterStartsWithValid(a);
return Boolean.TRUE;
});
}
约束的实现类则可直接在注解类中通过 @Constraint(validatedBy = {……}) 指定,本文在此不进行过多赘述。
5. 结语
Hibernate-Validator是一种较为优雅的数据校验实现方式,在日常的工作中已经得到了广泛的应用,希望本文能够帮助使用者更好更完善的从底层源码的角度了解Hibernate-Validator的功能,如果本文存在错误或遗漏请与作者联系,谢谢