【源码】Spring validation校验的核心类ValidatorImpl、MetaDataProvider和AnnotationMetaDataProvider源码分析

Spring validation参数校验系列

1、Spring validation参数校验基本使用

2、Spring validation参数校验之自定义校验规则及编程式校验等进阶篇

3、【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@RequestBody参数校验实现原理

4、【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@ModelAttribute及实体类参数校验实现原理

5、【源码】Spring validation参数校验原理解析之基本类型参数及Service层方法参数校验实现原理

6、【源码】Spring validation校验的核心类ValidatorImpl、MetaDataProvider和AnnotationMetaDataProvider源码分析

7、Spring validation参数校验高级篇之跨参数校验Cross-Parameter及分组序列校验@GroupSequenceProvider、@GroupSequence

8、【源码】Spring validation参数校验之跨参数校验Cross-Parameter原理分析

9、【源码】Spring validation参数校验之分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理

10、【源码】Spring validation参数校验实现原理总结

前言

限于篇幅,在前面的三篇讲解源码的文章中,对于一些类只做了简单的说明,这一篇专门用来讲解源码中比较关键的几个类。

温馨提醒:

Hibernate validation的设计比较复杂,要一次性全部分析清楚很困难,关联的细节很多。所以《Spring validation参数校验系列》文章通过从整体到细节,在每一篇中,不影响主题内容的情况下,穿插引入一些细节。在分享中,也会暂时忽略一些细节,留在下一篇讲解。建议如果本篇不太理解的,可以看看该系列的上一篇或者下一篇源码讲解文章。

ValidatorImpl

ValidatorImpl是Hibernate validation参数校验Validator接口的唯一实现。类的方法在前面的三篇Spring validation实现原理文章中有陆续分析到。这里从宏观的角度来看一下这个类。

ValidatorImpl.java

package org.hibernate.validator.internal.engine;

/**
 * 该类实现了Validator和ExecutableValidator接口
 */
public class ValidatorImpl implements Validator, ExecutableValidator {
}
该类实现了Validator和ExecutableValidator接口。

Validator.java

package javax.validation;

public interface Validator {

	/**
	 * 验证object中添加中的所有约束。校验失败,返回约束集合,成功返回空集合
	 */
	<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);

	/**
	 * 验证object对象中的propertyName属性的所有约束。校验失败,返回约束集合,成功返回空集合
	 */
	<T> Set<ConstraintViolation<T>> validateProperty(T object,
													 String propertyName,
													 Class<?>... groups);

	/**
	 * 验证beanType中的propertyName属性的value值是否满足所有施加的约束
	 */
	<T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType,
												  String propertyName,
												  Object value,
												  Class<?>... groups);

	/**
	 * 返回描述clazz约束的描述符对象。返回的对象(以及包括{@link ConstraintDescriptor}s在内的关联对象)是不可变的。
	 */
	BeanDescriptor getConstraintsForClass(Class<?> clazz);

	/**
	 * 返回指定类型的实例,该实例允许访问特定于提供程序的API。
	 */
	<T> T unwrap(Class<T> type);

	/**
	 * 返回用于验证约定方法或构造方法的参数及返回值信息。
	 */
	ExecutableValidator forExecutables();
}

ExecutableValidator.java

package javax.validation.executable;

/**
 * 用于验证方法或构造函数的参数和返回值
 */
public interface ExecutableValidator {

	/**
	 * 验证对给定方法的参数施加的所有约束
	 *
	 * @param <T> 要验证的方法所在的类类型
	 * @param object 要验证的方法的类对象
	 * @param method 验证参数约束的方法
	 * @param parameterValues 方法中的参数值
	 * @param groups 校验的分组。默认为Default
	 * @return 如果校验失败,返回失败集合;校验成功,返回空集合
	 * @throws IllegalArgumentException 如果传递的参数有问题,抛IllegalArgumentException异常
	 * @throws ValidationException 如果在验证过程中发生不可恢复的错误,抛ValidationException异常
	 */
	<T> Set<ConstraintViolation<T>> validateParameters(T object,
													   Method method,
													   Object[] parameterValues,
													   Class<?>... groups);

	/**
	 * 验证给定方法的返回值施加的所有约束。其他说明同上
	 */
	<T> Set<ConstraintViolation<T>> validateReturnValue(T object,
														Method method,
														Object returnValue,
														Class<?>... groups);

	/**
	 * 验证构造方法的参数施加的所有约束
	 */
	<T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor,
																  Object[] parameterValues,
																  Class<?>... groups);

	/**
	 * 验证构造方法的返回值施加的所有约束
	 */
	<T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor,
							   T createdObject, Class<?>... groups);
}

ValidatorImpl对外提供的方法为这两个接口的方法。即ValidatorImpl提供了属性校验、方法参数及返回值校验、构造方法参数校验。在前面的三篇源码中有介绍了部分实现。

MetaDataProvider

MetaDataProvider返回一个类中配置的约束、组序列等相关的配置信息。

MetaDataProvider.java

package org.hibernate.validator.internal.metadata.provider;

import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;

/**
 * 提供与约束相关的元数据,如约束、默认组序列等。
 * 实现基于不同的元数据源,如XML、编程映射和注释(BeanConfiguration枚举中定义)。
 * 元数据提供程序只返回直接为一个类配置的元数据,它们不处理合并超类或实现接口的元数据。
 */
public interface MetaDataProvider {

	/**
	 * 返回此提供程序配置的注释处理选项。
	 * AnnotationProcessingOptionsImpl为AnnotationProcessingOptions的唯一实现类,提供了xxxIgnoredFor()方法的接口
	 * (如:areClassLevelConstraintsIgnoredFor()、areParameterConstraintsIgnoredFor()等),用于配置哪些是不需要校验的
	 */
	AnnotationProcessingOptions getAnnotationProcessingOptions();


	/**
	 * 返回给定类型的bean配置,如果此提供程序没有给定类型的元数据,则返回null。
	 * BeanConfiguration用于存储来源于一个ConfigurationSource的一个Java类型的完整约束相关配置。
	 * 包含约束(字段、方法和类级别)上的元数据以及默认组序列上的元数据。
	 */
	<T> BeanConfiguration<? super T> getBeanConfiguration(Class<T> beanClass);
}

一个bean类的约束元数据信息都记录在BeanConfiguration中。

BeanConfiguration.java

package org.hibernate.validator.internal.metadata.raw;

import java.util.List;
import java.util.Set;

import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

/**
 * 表示源自一个ConfigurationSource的一个Java类型的完整约束相关配置。包含约束(字段、方法和类级别)上的元数据以及默认组序列上的元数据。
 */
public class BeanConfiguration<T> {

	// 枚举类。指定配置方式。有XML、Annotation、API,针对不同的配置方法,获取约束信息时,需要使用不同的方法
	// 例如Annotation方式,则通过读取注解信息;如果是XML,则需要解析XML标签
	private final ConfigurationSource source;
	
	// 对应的bean类
	private final Class<T> beanClass;

	// 校验的元素。有四个子类:
	//  ConstrainedField/ConstrainedType/ConstrainedParameter/ConstrainedExecutable,分别对应属性、类、参数及方法
	private final Set<ConstrainedElement> constrainedElements;

	// 分组序列
	private final List<Class<?>> defaultGroupSequence;

	// 动态分组序列的约定程序
	private final DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider;

    // BeanConfiguration只有上面这些属性,其他代码为对应属性的getter/setter及构造方法,此处省略
}

BeanConfiguration中记录了某个bean的属性、类、参数、方法添加的约束信息、配置来源、分组序列以及动态分组序列程序。

通过动态分组序列程序,可以根据不同的条件,修改某个约束使用哪个分组进行判断。例如积分维护,积分增加是消费、参加活动、购买等方式获得,积分减少是抵扣、过期等方式扣减,针对积分是增还是减,传入的类型是不同的。此时可以定义类型在增加和减少的枚举中为不同的组,然后自定义DefaultGroupSequenceProvider,在DefaultGroupSequenceProvider的getValidationGroups()方法中,根据是增加还是减少,返回不同的组。

ConfigurationSource.java

package org.hibernate.validator.internal.metadata.raw;

/**
 * 约束元数据信息的来源
 */
public enum ConfigurationSource {

	// 注解配置
	ANNOTATION( 0 ),
	// XML配置
	XML( 1 ),
	// 程序API配置
	API( 2 );
}

ConfigurationSource有三种来源,对应的MetaDataProvider有三个实现类,分别为:AnnotationMetaDataProvider、ProgrammaticMetaDataProvider、XmlMetaDataProvider,分别处理ConfigurationSource中的三种来源。在Spring及SpringBoot中,更多都是通过Annotation注解的方式实现。

AnnotationMetaDataProvider

AnnotationMetaDataProvider实现了MetaDataProvider接口,用于解析bean类中采用Annotation注解方式添加的约束元信息。

AnnotationMetaDataProvider.java

/*
 * Hibernate Validator, declare and validate application constraints
 *
 * License: Apache License, Version 2.0
 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
 */
package org.hibernate.validator.internal.metadata.provider;

import org.hibernate.validator.group.GroupSequenceProvider;
import org.hibernate.validator.internal.engine.ConstraintCreationContext;
import org.hibernate.validator.internal.engine.valueextraction.ArrayElement;
import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder;
import org.hibernate.validator.internal.metadata.core.*;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
import org.hibernate.validator.internal.metadata.raw.*;
import org.hibernate.validator.internal.properties.Callable;
import org.hibernate.validator.internal.properties.Constrainable;
import org.hibernate.validator.internal.properties.Getter;
import org.hibernate.validator.internal.properties.javabean.*;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.util.privilegedactions.*;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

import javax.validation.GroupSequence;
import javax.validation.Valid;
import javax.validation.groups.ConvertGroup;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.stream.Collectors;

import static org.hibernate.validator.internal.util.CollectionHelper.*;
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;

public class AnnotationMetaDataProvider implements MetaDataProvider {

    private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );

    private final ConstraintCreationContext constraintCreationContext;
    private final AnnotationProcessingOptions annotationProcessingOptions;
    private final JavaBeanHelper javaBeanHelper;

    // BeanConfiguration中记录了某个bean的属性、类、参数、方法添加的约束信息、配置来源、分组序列以及动态分组序列程序
    private final BeanConfiguration<Object> objectBeanConfiguration;

    public AnnotationMetaDataProvider(ConstraintCreationContext constraintCreationContext,
                                      JavaBeanHelper javaBeanHelper,
                                      AnnotationProcessingOptions annotationProcessingOptions) {
        this.constraintCreationContext = constraintCreationContext;
        this.javaBeanHelper = javaBeanHelper;
        this.annotationProcessingOptions = annotationProcessingOptions;

        this.objectBeanConfiguration = retrieveBeanConfiguration( Object.class );
    }

    /**
     * 返回一个AnnotationProcessingOptionsImpl对象,记录哪些类、方法、参数是不需要校验的
     */
    @Override
    public AnnotationProcessingOptions getAnnotationProcessingOptions() {
        return new AnnotationProcessingOptionsImpl();
    }

    /**
     * 获取bean类中定义的属性、类、参数、方法添加的约束信息、配置来源、分组序列以及动态分组序列程序
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> BeanConfiguration<T> getBeanConfiguration(Class<T> beanClass) {
        if ( Object.class.equals( beanClass ) ) {
            return (BeanConfiguration<T>) objectBeanConfiguration;
        }
        return retrieveBeanConfiguration( beanClass );
    }

    /**
     * 检索bean类的元数据信息
     * @param beanClass The bean class for which to retrieve the meta data
     *
     * @return Retrieves constraint related meta data from the annotations of the given type.
     */
    private <T> BeanConfiguration<T> retrieveBeanConfiguration(Class<T> beanClass) {
        // 获取类中定义的属性添加的约束元素信息
        Set<ConstrainedElement> constrainedElements = getFieldMetaData( beanClass );
        // 获取类中定义的方法添加的约束元素信息
        constrainedElements.addAll( getMethodMetaData( beanClass ) );
        // 获取类的构造方法添加的约束元素信息
        constrainedElements.addAll( getConstructorMetaData( beanClass ) );

        // 获取类级添加的约束信息
        Set<MetaConstraint<?>> classLevelConstraints = getClassLevelConstraints( beanClass );
        if ( !classLevelConstraints.isEmpty() ) {
            ConstrainedType classLevelMetaData =
                    new ConstrainedType(
                            ConfigurationSource.ANNOTATION,
                            beanClass,
                            classLevelConstraints
                    );
            constrainedElements.add( classLevelMetaData );
        }

        // 封装成BeanConfiguration对象,来源为Annotation
        return new BeanConfiguration<>(
                ConfigurationSource.ANNOTATION,
                beanClass,
                constrainedElements,
                getDefaultGroupSequence( beanClass ),
                getDefaultGroupSequenceProvider( beanClass )
        );
    }

    /**
     * 获取bean中添加的@GroupSequence注解的组序列。组序列中的组顺序执行,排前面的组对应的注解约束会先判断,如果前面的失败了,不会校验排后面的组
     * @param beanClass
     * @return
     */
    private List<Class<?>> getDefaultGroupSequence(Class<?> beanClass) {
        GroupSequence groupSequenceAnnotation = beanClass.getAnnotation( GroupSequence.class );
        return groupSequenceAnnotation != null ? Arrays.asList( groupSequenceAnnotation.value() ) : null;
    }

    /**
     * 获取类中定义的动态分组的约定程序
     * @param beanClass
     * @param <T>
     * @return
     */
    private <T> DefaultGroupSequenceProvider<? super T> getDefaultGroupSequenceProvider(Class<T> beanClass) {
        // 判断对应的bean是否添加了GroupSequenceProvider注解
        GroupSequenceProvider groupSequenceProviderAnnotation = beanClass.getAnnotation( GroupSequenceProvider.class );
        // 只有添加了GroupSequenceProvider注解才会返回动态分组的约定程序
        if ( groupSequenceProviderAnnotation != null ) {
            // 获取GroupSequenceProvider注解中指定的约定程序类,该类实现了DefaultGroupSequenceProvider接口
            @SuppressWarnings("unchecked")
            Class<? extends DefaultGroupSequenceProvider<? super T>> providerClass =
                    (Class<? extends DefaultGroupSequenceProvider<? super T>>) groupSequenceProviderAnnotation.value();
            // 创建一个自定义的DefaultGroupSequenceProvider实例
            return newGroupSequenceProviderClassInstance( beanClass, providerClass );
        }

        return null;
    }

    /**
     * 创建一个DefaultGroupSequenceProvider实例,getValidationGroups()方法的参数必现是beanClass类型
     * @param beanClass
     * @param providerClass
     * @param <T>
     * @return
     */
    private <T> DefaultGroupSequenceProvider<? super T> newGroupSequenceProviderClassInstance(Class<T> beanClass,
                                                                                              Class<? extends DefaultGroupSequenceProvider<? super T>> providerClass) {
        Method[] providerMethods = run( GetMethods.action( providerClass ) );
        for ( Method method : providerMethods ) {
            Class<?>[] paramTypes = method.getParameterTypes();
            // 判断DefaultGroupSequenceProvider接口的getValidationGroups()方法的参数是否为beanClass类型
            if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge()
                    && paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) {

                return run(
                        NewInstance.action( providerClass, "the default group sequence provider" )
                );
            }
        }

        throw LOG.getWrongDefaultGroupSequenceProviderTypeException( beanClass );
    }

    /**
     * 获取类级添加的约束信息
     * @param clazz
     * @return
     */
    private Set<MetaConstraint<?>> getClassLevelConstraints(Class<?> clazz) {
        if ( annotationProcessingOptions.areClassLevelConstraintsIgnoredFor( clazz ) ) {
            return Collections.emptySet();
        }

        // 获取类级定义的约束信息
        List<ConstraintDescriptorImpl<?>> classLevelConstraintDescriptors = findConstraints( null, clazz.getDeclaredAnnotations(),
                ConstraintLocationKind.TYPE );

        if ( classLevelConstraintDescriptors.isEmpty() ) {
            return Collections.emptySet();
        }

        Set<MetaConstraint<?>> classLevelConstraints = newHashSet( classLevelConstraintDescriptors.size() );
        ConstraintLocation location = ConstraintLocation.forClass( clazz );

        // 封装为MetaConstraints对象
        for ( ConstraintDescriptorImpl<?> constraintDescriptor : classLevelConstraintDescriptors ) {
            classLevelConstraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
                    constraintCreationContext.getValueExtractorManager(),
                    constraintCreationContext.getConstraintValidatorManager(),
                    constraintDescriptor,
                    location ) );
        }

        return classLevelConstraints;
    }

    /**
     * 获取bean类中定义的属性添加的约束元数据
     * @param beanClass
     * @return
     */
    private Set<ConstrainedElement> getFieldMetaData(Class<?> beanClass) {
        Set<ConstrainedElement> propertyMetaData = newHashSet();

        // 遍历类中的属性
        for ( Field field : run( GetDeclaredFields.action( beanClass ) ) ) {
            // HV-172
            if ( Modifier.isStatic( field.getModifiers() ) || field.isSynthetic() ) {
                continue;
            }

            JavaBeanField javaBeanField = javaBeanHelper.field( field );

            if ( annotationProcessingOptions.areMemberConstraintsIgnoredFor( javaBeanField ) ) {
                continue;
            }

            // 调用findPropertyMetaData()获取属性中添加的约束元数据
            propertyMetaData.add( findPropertyMetaData( javaBeanField ) );
        }
        return propertyMetaData;
    }

    /**
     * 查找类属性的约束元数据,封装成ConstrainedField对象并返回
     * @param javaBeanField
     * @return
     */
    private ConstrainedField findPropertyMetaData(JavaBeanField javaBeanField) {
        // 查找给定属性添加的Validator相关的约束信息,并封装成MetaConstraint集合
        Set<MetaConstraint<?>> constraints = convertToMetaConstraints(
                findConstraints( javaBeanField, ConstraintLocationKind.FIELD ),
                javaBeanField
        );

        // 找级联元数据。通过判断添加注解的元素是否有添加@Valid,如果有为级联,返回的CascadingMetaDataBuilder的cascading为true
        CascadingMetaDataBuilder cascadingMetaDataBuilder = findCascadingMetaData( javaBeanField );

        Set<MetaConstraint<?>> typeArgumentsConstraints = findTypeAnnotationConstraints( javaBeanField );

        return new ConstrainedField(
                ConfigurationSource.ANNOTATION,
                javaBeanField,
                constraints,
                typeArgumentsConstraints,
                cascadingMetaDataBuilder
        );
    }

    /**
     * 将ConstraintDescriptorImpl集合对象转换为MetaConstraint集合
     * @param constraintDescriptors
     * @param javaBeanField
     * @return
     */
    private Set<MetaConstraint<?>> convertToMetaConstraints(List<ConstraintDescriptorImpl<?>> constraintDescriptors, JavaBeanField javaBeanField) {
        if ( constraintDescriptors.isEmpty() ) {
            return Collections.emptySet();
        }

        Set<MetaConstraint<?>> constraints = newHashSet( constraintDescriptors.size() );

        ConstraintLocation location = ConstraintLocation.forField( javaBeanField );

        for ( ConstraintDescriptorImpl<?> constraintDescription : constraintDescriptors ) {
            constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
                    constraintCreationContext.getValueExtractorManager(),
                    constraintCreationContext.getConstraintValidatorManager(),
                    constraintDescription, location ) );
        }
        return constraints;
    }

    private Set<ConstrainedExecutable> getConstructorMetaData(Class<?> clazz) {
        Executable[] declaredConstructors = run( GetDeclaredConstructors.action( clazz ) );

        return getMetaData( declaredConstructors );
    }

    /**
     * 获取clazz定义的所有方法,调用getMetaData(),遍历所有方法,解析方法中添加的约束
     * @param clazz
     * @return
     */
    private Set<ConstrainedExecutable> getMethodMetaData(Class<?> clazz) {
        Executable[] declaredMethods = run( GetDeclaredMethods.action( clazz ) );

        return getMetaData( declaredMethods );
    }

    /**
     * 遍历方法,获取约束信息
     * @param executableElements
     * @return
     */
    private Set<ConstrainedExecutable> getMetaData(Executable[] executableElements) {
        Set<ConstrainedExecutable> executableMetaData = newHashSet();

        for ( Executable executable : executableElements ) {
            // HV-172; ignoring synthetic methods (inserted by the compiler), as they can't have any constraints
            // anyway and possibly hide the actual method with the same signature in the built meta model
            if ( Modifier.isStatic( executable.getModifiers() ) || executable.isSynthetic() ) {
                continue;
            }

            executableMetaData.add( findExecutableMetaData( executable ) );
        }

        return executableMetaData;
    }

    /**
     * 查找给定的方法或构造器中添加的所有约束
     * Finds all constraint annotations defined for the given method or constructor.
     *
     * @param executable The executable element to check for constraints annotations.
     *
     * @return A meta data object describing the constraints specified for the
     * given element.
     */
    private ConstrainedExecutable findExecutableMetaData(Executable executable) {
        JavaBeanExecutable<?> javaBeanExecutable = javaBeanHelper.executable( executable );
        // 获取方法的入参中添加的约束
        List<ConstrainedParameter> parameterConstraints = getParameterMetaData( javaBeanExecutable );

        // 获取方法添加的约束
        Map<ConstraintType, List<ConstraintDescriptorImpl<?>>> executableConstraints = findConstraints(
                javaBeanExecutable,
                ConstraintLocationKind.of( javaBeanExecutable.getConstrainedElementKind() )
        ).stream().collect( Collectors.groupingBy( ConstraintDescriptorImpl::getConstraintType ) );

        Set<MetaConstraint<?>> crossParameterConstraints;
        if ( annotationProcessingOptions.areCrossParameterConstraintsIgnoredFor( javaBeanExecutable ) ) {
            crossParameterConstraints = Collections.emptySet();
        }
        else {
            crossParameterConstraints = convertToMetaConstraints(
                    executableConstraints.get( ConstraintType.CROSS_PARAMETER ),
                    javaBeanExecutable
            );
        }

        Set<MetaConstraint<?>> returnValueConstraints;
        Set<MetaConstraint<?>> typeArgumentsConstraints;
        CascadingMetaDataBuilder cascadingMetaDataBuilder;

        if ( annotationProcessingOptions.areReturnValueConstraintsIgnoredFor( javaBeanExecutable ) ) {
            returnValueConstraints = Collections.emptySet();
            typeArgumentsConstraints = Collections.emptySet();
            cascadingMetaDataBuilder = CascadingMetaDataBuilder.nonCascading();
        }
        else {
            // 获取方法返回值添加的约束信息
            typeArgumentsConstraints = findTypeAnnotationConstraints( javaBeanExecutable );
            returnValueConstraints = convertToMetaConstraints(
                    executableConstraints.get( ConstraintType.GENERIC ),
                    javaBeanExecutable
            );
            cascadingMetaDataBuilder = findCascadingMetaData( javaBeanExecutable );
        }

        // 封装成ConstrainedExecutable对象
        return new ConstrainedExecutable(
                ConfigurationSource.ANNOTATION,
                javaBeanExecutable,
                parameterConstraints,
                crossParameterConstraints,
                returnValueConstraints,
                typeArgumentsConstraints,
                cascadingMetaDataBuilder
        );
    }

    private Set<MetaConstraint<?>> convertToMetaConstraints(List<ConstraintDescriptorImpl<?>> constraintDescriptors, Callable callable) {
        if ( constraintDescriptors == null || constraintDescriptors.isEmpty() ) {
            return Collections.emptySet();
        }

        Set<MetaConstraint<?>> constraints = newHashSet( constraintDescriptors.size() );

        ConstraintLocation returnValueLocation = ConstraintLocation.forReturnValue( callable );
        ConstraintLocation crossParameterLocation = ConstraintLocation.forCrossParameter( callable );

        for ( ConstraintDescriptorImpl<?> constraintDescriptor : constraintDescriptors ) {
            ConstraintLocation location = constraintDescriptor.getConstraintType() == ConstraintType.GENERIC
                    ? returnValueLocation
                    : crossParameterLocation;
            constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
                    constraintCreationContext.getValueExtractorManager(),
                    constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor, location ) );
        }

        return constraints;
    }

    /**
     * 检索给定的方法的参数添加的约束注解信息
     * Retrieves constraint related meta data for the parameters of the given
     * executable.
     *
     * @param javaBeanExecutable The executable of interest.
     *
     * @return A list with parameter meta data for the given executable.
     */
    private List<ConstrainedParameter> getParameterMetaData(JavaBeanExecutable<?> javaBeanExecutable) {
        if ( !javaBeanExecutable.hasParameters() ) {
            return Collections.emptyList();
        }

        List<JavaBeanParameter> parameters = javaBeanExecutable.getParameters();

        List<ConstrainedParameter> metaData = new ArrayList<>( parameters.size() );

        int i = 0;
        // 遍历每个参数
        for ( JavaBeanParameter parameter : parameters ) {
            // 判断该参数是否无需考虑约束条件的类型
            if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( javaBeanExecutable, i ) ) {
                metaData.add(
                        new ConstrainedParameter(
                                ConfigurationSource.ANNOTATION,
                                javaBeanExecutable,
                                parameter.getGenericType(),
                                i,
                                Collections.emptySet(),
                                Collections.emptySet(),
                                CascadingMetaDataBuilder.nonCascading()
                        )
                );
                i++;
                continue;
            }

            // 查找方法参数添加的所有约束注释,并在约束描述符列表中返回它们。
            List<ConstraintDescriptorImpl<?>> constraintDescriptors = findConstraints( javaBeanExecutable, parameter, ConstraintLocationKind.PARAMETER );
            Set<MetaConstraint<?>> parameterConstraints;

            if ( !constraintDescriptors.isEmpty() ) {
                parameterConstraints = newHashSet( constraintDescriptors.size() );
                ConstraintLocation location = ConstraintLocation.forParameter( javaBeanExecutable, i );

                // 遍历添加的约束,封装成MetaConstraints对象
                for ( ConstraintDescriptorImpl<?> constraintDescriptorImpl : constraintDescriptors ) {
                    parameterConstraints.add(
                            MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
                                    constraintCreationContext.getValueExtractorManager(),
                                    constraintCreationContext.getConstraintValidatorManager(), constraintDescriptorImpl,
                                    location ) );
                }
            }
            else {
                parameterConstraints = Collections.emptySet();
            }

            Set<MetaConstraint<?>> typeArgumentsConstraints = findTypeAnnotationConstraintsForExecutableParameter( javaBeanExecutable, parameter );
            // 通过判断添加注解的元素是否有添加@Valid,如果有为级联,返回的CascadingMetaDataBuilder的cascading为true
            CascadingMetaDataBuilder cascadingMetaData = findCascadingMetaData( parameter );

            // 将信息封装成ConstrainedParameter对象
            metaData.add(
                    new ConstrainedParameter(
                            ConfigurationSource.ANNOTATION,
                            javaBeanExecutable,
                            parameter.getGenericType(),
                            i,
                            parameterConstraints,
                            typeArgumentsConstraints,
                            cascadingMetaData
                    )
            );
            i++;
        }

        return metaData;
    }

    /**
     * 查找为给定可约束项定义的所有约束注释,并在约束描述符列表中返回它们。
     */
    private List<ConstraintDescriptorImpl<?>> findConstraints(JavaBeanAnnotatedConstrainable constrainable, ConstraintLocationKind kind) {
        return findConstraints( constrainable, constrainable, kind );
    }

    /**
     * 查找为给定可约束项定义的所有约束注释,并在约束描述符列表中返回它们。
     */
    private List<ConstraintDescriptorImpl<?>> findConstraints(Constrainable constrainable, JavaBeanAnnotatedElement annotatedElement,
                                                              ConstraintLocationKind kind) {
        List<ConstraintDescriptorImpl<?>> metaData = newArrayList();
        // 获取元素添加的所有注解,遍历
        for ( Annotation annotation : annotatedElement.getDeclaredAnnotations() ) {
            // 调用findConstraintAnnotations()方法,查找约束性注解信息,并返回
            metaData.addAll( findConstraintAnnotations( constrainable, annotation, kind ) );
        }

        return metaData;
    }

    /**
     * 查找为给定可约束项定义的所有约束注释,并在约束描述符列表中返回它们。
     */
    private List<ConstraintDescriptorImpl<?>> findConstraints(Constrainable constrainable, Annotation[] annotations,
                                                              ConstraintLocationKind kind) {
        if ( annotations.length == 0 ) {
            return Collections.emptyList();
        }

        List<ConstraintDescriptorImpl<?>> metaData = newArrayList();
        for ( Annotation annotation : annotations ) {
            metaData.addAll( findConstraintAnnotations( constrainable, annotation, kind ) );
        }

        return metaData;
    }

    /**
     * 检查给定的注释,看看它是单值约束注释还是多值约束注释
     */
    protected <A extends Annotation> List<ConstraintDescriptorImpl<?>> findConstraintAnnotations(
            Constrainable constrainable,
            A annotation,
            ConstraintLocationKind type) {

        // HV-1049 and HV-1311 - Ignore annotations from the JDK (jdk.internal.* and java.*); They cannot be constraint
        // annotations so skip them right here, as for the proper check we'd need package access permission for
        // "jdk.internal" and "java".
        /**
         * HV-1049和HV-1311-忽略JDK(JDK.internal.和java.)中的注释;它们不能是约束注释,所以请跳过它们,因为为了进行正确的检查,
         * 需要“jdk.internal”和“java”的包访问权限。
         */
        if ( constraintCreationContext.getConstraintHelper().isJdkAnnotation( annotation.annotationType() ) ) {
            return Collections.emptyList();
        }

        List<Annotation> constraints = newArrayList();
        Class<? extends Annotation> annotationType = annotation.annotationType();
        /**
         * ConstraintHelper.isConstraintAnnotation()【执行constraintCreationContext.getConstraintHelper().
         * isConstraintAnnotation( annotationType )】 -> isBuiltinConstraint() -> BuiltinConstraint.isBuiltin()
         * 【BuiltinConstraint为枚举类,列举了validation的所有约束字段。从而判断当前的参数是否添加了约束】
         */
        if ( constraintCreationContext.getConstraintHelper().isConstraintAnnotation( annotationType ) ) {
            constraints.add( annotation );
        }
        // 暂时没有用到。用于判断多值约束
        else if ( constraintCreationContext.getConstraintHelper().isMultiValueConstraint( annotationType ) ) {
            constraints.addAll( constraintCreationContext.getConstraintHelper().getConstraintsFromMultiValueConstraint( annotation ) );
        }

        return constraints.stream()
                .map( c -> buildConstraintDescriptor( constrainable, c, type ) )
                .collect( Collectors.toList() );
    }

    private Map<Class<?>, Class<?>> getGroupConversions(AnnotatedType annotatedType) {
        return getGroupConversions(
                annotatedType.getAnnotation( ConvertGroup.class ),
                annotatedType.getAnnotation( ConvertGroup.List.class )
        );
    }

    private Map<Class<?>, Class<?>> getGroupConversions(ConvertGroup groupConversion, ConvertGroup.List groupConversionList) {
        if ( groupConversion == null && ( groupConversionList == null || groupConversionList.value().length == 0 ) ) {
            return Collections.emptyMap();
        }

        Map<Class<?>, Class<?>> groupConversions = newHashMap();

        if ( groupConversion != null ) {
            groupConversions.put( groupConversion.from(), groupConversion.to() );
        }

        if ( groupConversionList != null ) {
            for ( ConvertGroup conversion : groupConversionList.value() ) {
                if ( groupConversions.containsKey( conversion.from() ) ) {
                    throw LOG.getMultipleGroupConversionsForSameSourceException(
                            conversion.from(),
                            CollectionHelper.<Class<?>>asSet(
                                    groupConversions.get( conversion.from() ),
                                    conversion.to()
                            )
                    );
                }

                groupConversions.put( conversion.from(), conversion.to() );
            }
        }

        return groupConversions;
    }

    private <A extends Annotation> ConstraintDescriptorImpl<A> buildConstraintDescriptor(Constrainable constrainable,
                                                                                         A annotation,
                                                                                         ConstraintLocationKind type) {
        return new ConstraintDescriptorImpl<>(
                constraintCreationContext.getConstraintHelper(),
                constrainable,
                new ConstraintAnnotationDescriptor<>( annotation ),
                type
        );
    }

    /**
     * 运行给定的特权操作,必要时使用特权块
     */
    private <T> T run(PrivilegedAction<T> action) {
        return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
    }

    /**
     * 查找字段的类型参数约束
     */
    protected Set<MetaConstraint<?>> findTypeAnnotationConstraints(JavaBeanField javaBeanField) {
        return findTypeArgumentsConstraints(
                javaBeanField,
                new TypeArgumentFieldLocation( javaBeanField ),
                javaBeanField.getAnnotatedType()
        );
    }

    /**
     * 查找方法返回值的类型参数约束。
     */
    protected Set<MetaConstraint<?>> findTypeAnnotationConstraints(JavaBeanExecutable<?> javaBeanExecutable) {
        return findTypeArgumentsConstraints(
                javaBeanExecutable,
                new TypeArgumentReturnValueLocation( javaBeanExecutable ),
                // 获取方法返回值的类型。如果是普通的Method,则最后执行Method.getAnnotatedReturnType();
                // 如果是构造方法,执行Constructor.getAnnotatedReturnType()方法
                javaBeanExecutable.getAnnotatedType()
        );
    }

    /**
     * 通过判断添加注解的元素是否有添加@Valid,如果有为级联,返回的CascadingMetaDataBuilder的cascading为true
     */
    private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanParameter javaBeanParameter) {
        Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanParameter.getAnnotatedType(),
                javaBeanParameter.getTypeParameters() );

        try {
            return getCascadingMetaData( javaBeanParameter, containerElementTypesCascadingMetaData );
        }
        catch (ArrayIndexOutOfBoundsException ex) {
            LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
            return CascadingMetaDataBuilder.nonCascading();
        }
    }

    /**
     * 查找级联元数据。通过判断添加注解的元素是否有添加@Valid,如果有为级联,返回的CascadingMetaDataBuilder的cascading为true
     * @param javaBeanField
     * @return
     */
    private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanField javaBeanField) {
        Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata(
                javaBeanField.getAnnotatedType(),
                // 获取属性泛型类或接口的泛型声明所声明的类型参数。如ArrayList<String> list属性值,此方法返回[String]
                javaBeanField.getTypeParameters() );

        return getCascadingMetaData( javaBeanField, containerElementTypesCascadingMetaData );
    }

    /**
     * 查找级联元数据。通过判断添加注解的元素是否有添加@Valid,如果有为级联,返回的CascadingMetaDataBuilder的cascading为true
     * @param javaBeanExecutable
     * @return
     */
    private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanExecutable<?> javaBeanExecutable) {
        Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanExecutable.getAnnotatedType(),
                javaBeanExecutable.getTypeParameters() );

        return getCascadingMetaData( javaBeanExecutable, containerElementTypesCascadingMetaData );
    }

    private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetadata(AnnotatedType annotatedType,
                                                                                              TypeVariable<?>[] typeParameters) {
        // 如果是AnnotatedArrayType,返回空Map
        if ( annotatedType instanceof AnnotatedArrayType ) {
            return getTypeParametersCascadingMetaDataForArrayType( (AnnotatedArrayType) annotatedType );
        }
        // 被注解的参数化类型
        else if ( annotatedType instanceof AnnotatedParameterizedType ) {
            return getTypeParametersCascadingMetaDataForParameterizedType( (AnnotatedParameterizedType) annotatedType, typeParameters );
        }
        else {
            return Collections.emptyMap();
        }
    }

    /**
     * 获取参数化类型的类型参数级联元数据
     */
    private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForParameterizedType(
            AnnotatedParameterizedType annotatedParameterizedType, TypeVariable<?>[] typeParameters) {
        Map<TypeVariable<?>, CascadingMetaDataBuilder> typeParametersCascadingMetadata = CollectionHelper.newHashMap( typeParameters.length );

        AnnotatedType[] annotatedTypeArguments = annotatedParameterizedType.getAnnotatedActualTypeArguments();
        int i = 0;

        for ( AnnotatedType annotatedTypeArgument : annotatedTypeArguments ) {
            Map<TypeVariable<?>, CascadingMetaDataBuilder> nestedTypeParametersCascadingMetadata = getTypeParametersCascadingMetaDataForAnnotatedType(
                    annotatedTypeArgument );

            typeParametersCascadingMetadata.put( typeParameters[i], new CascadingMetaDataBuilder( annotatedParameterizedType.getType(), typeParameters[i],
                    annotatedTypeArgument.isAnnotationPresent( Valid.class ), nestedTypeParametersCascadingMetadata,
                    getGroupConversions( annotatedTypeArgument ) ) );
            i++;
        }

        return typeParametersCascadingMetadata;
    }

    private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForArrayType(AnnotatedArrayType annotatedArrayType) {
        return Collections.emptyMap();
    }

    private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForAnnotatedType(AnnotatedType annotatedType) {
        if ( annotatedType instanceof AnnotatedArrayType ) {
            return getTypeParametersCascadingMetaDataForArrayType( (AnnotatedArrayType) annotatedType );
        }
        else if ( annotatedType instanceof AnnotatedParameterizedType ) {
            return getTypeParametersCascadingMetaDataForParameterizedType( (AnnotatedParameterizedType) annotatedType,
                    ReflectionHelper.getClassFromType( annotatedType.getType() ).getTypeParameters() );
        }
        else {
            return Collections.emptyMap();
        }
    }

    /**
     * 查找参数的类型实参约束
     */
    protected Set<MetaConstraint<?>> findTypeAnnotationConstraintsForExecutableParameter(JavaBeanExecutable<?> javaBeanExecutable,
                                                                                         JavaBeanParameter javaBeanParameter) {
        try {
            return findTypeArgumentsConstraints(
                    javaBeanExecutable,
                    new TypeArgumentExecutableParameterLocation( javaBeanExecutable, javaBeanParameter.getIndex() ),
                    javaBeanParameter.getAnnotatedType()
            );
        }
        catch (ArrayIndexOutOfBoundsException ex) {
            LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
            return Collections.emptySet();
        }
    }

    /**
     * 通过判断添加注解的元素是否有添加@Valid,如果有为级联,返回的CascadingMetaDataBuilder的cascading为true
     * @param annotatedElement
     * @param containerElementTypesCascadingMetaData
     * @return
     */
    private CascadingMetaDataBuilder getCascadingMetaData(JavaBeanAnnotatedElement annotatedElement,
                                                          Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) {
        return CascadingMetaDataBuilder.annotatedObject( annotatedElement.getType(), annotatedElement.isAnnotationPresent( Valid.class ),
                containerElementTypesCascadingMetaData, getGroupConversions( annotatedElement.getAnnotatedType() ) );
    }


}

Spring validation参数校验原理解析之Controller控制器参数校验中@RequestBody参数校验实现原理-CSDN博客

的3.1中介绍了BeanMetaDataManagerImpl的getBeanMetaData(Class<T> beanClass)方法,对应的beanClass首次调用时,会先调用createBeanMetaData()方法,调用getBeanConfigurationForHierarchy()方法,遍历beanClass及其父类,执行provider.getBeanConfiguration(clazz),从而执行AnnotationMetaDataProvider的getBeanConfiguration()方法,获取beanClass对应的约束元数据信息。

getFieldMetaData(beanClass):遍历类中定义的属性,解析属性中添加的约束注解,封装成ConstrainedElement对象;

getMethodMetaData(beanClass):遍历类中定义的方法,排查静态方法和合成方法,解析方法添加的约束注解、入参及返回值(自动判断是作用在入参还是返回值上),封装成ConstrainedExecutable对象。ConstrainedExecutable也实现了ConstrainedElement接口;

getConstructorMetaData(beanClass):遍历构造方法,处理同getMethodMetaData(beanClass);

getClassLevelConstraints(beanClass):解析类中添加的约束注解,封装成COnstraintDescriptor对象;最终转换成ConstrainedType对象。ConstrainedType也实现了ConstrainedElement接口;

在findConstraintAnnotations(Constrainable constrainable,A annotation,ConstraintLocationKind type)方法中,会执行ConstraintHelper.isConstraintAnnotation()【执行constraintCreationContext.getConstraintHelper().isConstraintAnnotation( annotationType )】

    /**
	 * ConstraintHelper类的isConstraintAnnotation()方法。判断annotationType是否为约束注解
	 */
    public boolean isConstraintAnnotation(Class<? extends Annotation> annotationType) {
		// BuiltinConstraint.isBuiltin()【BuiltinConstraint为枚举类,列举了validation的所有约束字段。从而判断当前的参数是否为BuiltinConstraint枚举中的注解】
		if ( isBuiltinConstraint( annotationType ) ) {
			return true;
		}
		// 判断annotationType是否添加了@Constraint注解
		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;
		} );
	}

isBuiltinConstraint() -> BuiltinConstraint.isBuiltin()。BuiltinConstraint为枚举类,列举了validation的所有约束字段。从而判断当前的参数是否属于BuiltinConstraint枚举中的注解。

BuiltinConstraint.java

enum BuiltinConstraint {

	JAVAX_VALIDATION_CONSTRAINTS_MIN("javax.validation.constraints.Min"),
	JAVAX_VALIDATION_CONSTRAINTS_MAX("javax.validation.constraints.Max"),
	JAVAX_VALIDATION_CONSTRAINTS_NEGATIVE("javax.validation.constraints.Negative"),
	JAVAX_VALIDATION_CONSTRAINTS_NEGATIVE_OR_ZERO("javax.validation.constraints.NegativeOrZero"),
	JAVAX_VALIDATION_CONSTRAINTS_NOT_BLANK("javax.validation.constraints.NotBlank"),
	JAVAX_VALIDATION_CONSTRAINTS_NOT_EMPTY("javax.validation.constraints.NotEmpty"),
	JAVAX_VALIDATION_CONSTRAINTS_NOT_NULL("javax.validation.constraints.NotNull"),
	JAVAX_VALIDATION_CONSTRAINTS_NULL("javax.validation.constraints.Null"),

    // 省略其他,只列出部分
}

结尾

以上为本次分享的Spring validation参加校验的核心类的源码分析。限于篇幅,本篇就先分享到这里,希望对你有所帮助。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
下面是将这个SQL查询改成使用JdbcTemplate执行的代码: ``` List<List<String>> assetscardcodeQueryList = new ArrayList<>(); ArrayList<String> query1 = new ArrayList<>(); ArrayList<String> query2 = new ArrayList<>(); query1.add("000000009247"); query2.add("000000009247"); assetscardcodeQueryList.add(query1); assetscardcodeQueryList.add(query2); List<List<String>> assetscardcodeAndBukrsQueryList = new ArrayList<>(); ArrayList<String> query3 = new ArrayList<>(); query3.add("400000001644"); query3.add("A017"); query3.add("400000001641"); query3.add("A017"); String sql = "select * from table where (cloum1,cloum2) in " + "(?, ?)" + ", ".repeat(assetscardcodeAndBukrsQueryList.size() - 1) + "or cloum1 in " + "(?" + ", ?".repeat(assetscardcodeQueryList.size() - 1) + ")"; List<Object> params = new ArrayList<>(); for (List<String> cloumPair : assetscardcodeAndBukrsQueryList) { params.addAll(cloumPair); } params.addAll(assetscardcodeQueryList.stream().flatMap(Collection::stream).collect(Collectors.toList())); List<Map<String, Object>> rows = jdbcTemplate.query(sql, params.toArray(), new RowMapper<Map<String, Object>>() { @Override public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException { ResultSetMetaData meta = rs.getMetaData(); Map<String, Object> row = new HashMap<>(); for (int i = 1; i <= meta.getColumnCount(); i++) { row.put(meta.getColumnName(i), rs.getObject(i)); } return row; } }); // 处理查询结果 for (Map<String, Object> row : rows) { // do something } ``` 这里使用了与前面相同的动态生成SQL语句和参数设置方式,只是将参数列表改成了从Java对象中获取。最终,该方法将返回一个包含查询结果的列表,每个结果都是一个Map对象,其中包含列名和对应的值。你可以根据需要处理该结果集。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值