IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot中@Vlidated注解校验源码 -> 正文阅读

[Java知识库]springboot中@Vlidated注解校验源码

????????springboot中支持JSR-303校验规范,该规范的默认实现为hibernate,关于常用校验注解的使用参考:springboot中注解校验@Valid@Validated(亲测有效)_卖柴火的小伙子的博客-CSDN博客

本文重在原理,下面从常用的两种注解校验方式看一下源码原理.

1.请求单个参数注解

? ? ? ? 此种实现方式是利用cglib动态代理动态获取方法拦截切面,主要逻辑在MethodValidationInterceptor.java


	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
		if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
			return invocation.proceed();
		}
        
        // 获取标注在方法或是类上的@Vlidated注解中的value属性值.@Validated中value值用于分组校验的标识.
		Class<?>[] groups = determineValidationGroups(invocation);

		// ExecutableValidator是hibernate校验的核心处理类
		ExecutableValidator execVal = this.validator.forExecutables();
        // 获取拦截的方法class对象
		Method methodToValidate = invocation.getMethod();
		Set<ConstraintViolation<Object>> result;

        // 获取方法拦截的所在的class类对象
		Object target = invocation.getThis();
		Assert.state(target != null, "Target must not be null");

		try {
            // 调用hibernate校验的核心处理类进行请求参数校验
			result = execVal.validateParameters(target, methodToValidate, invocation.getArguments(), groups);
		}
		catch (IllegalArgumentException ex) {
			// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
			// Let's try to find the bridged method on the implementation class...
			methodToValidate = BridgeMethodResolver.findBridgedMethod(
					ClassUtils.getMostSpecificMethod(invocation.getMethod(), target.getClass()));
			result = execVal.validateParameters(target, methodToValidate, invocation.getArguments(), groups);
		}
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}
        // 方法拦截执行结束返回参数
		Object returnValue = invocation.proceed();
        // 调用hibernate校验的核心处理类进行响应参数校验,并返回响应参数
		result = execVal.validateReturnValue(target, methodToValidate, returnValue, groups);
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}

		return returnValue;
	}

// 获取所在的类或是方法上的@Vlidated注解的value属性值.@Validated中value值用于分组校验的标识.
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
    // 首先获取方法上是否有@Valited注解,如果没有则看类上是否有此注解.		
Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class);
		if (validatedAnn == null) {
			Object target = invocation.getThis();
			Assert.state(target != null, "Target must not be null");
			validatedAnn = AnnotationUtils.findAnnotation(target.getClass(), Validated.class);
		}
        // 此处获取的是@Validated注解中标注的value属性值.
		return (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
	}

ValidatorImpl.java----注解校验的实现类

private <T> Set<ConstraintViolation<T>> validateParameters(T object, ExecutableElement executable, Object[] parameterValues, Class<?>... groups) {
		//this might be the case for parameterless methods
		if ( parameterValues == null ) {
			return Collections.emptySet();
		}
        
        // 根据@Validated注解的属性构建校验顺序处理器
		ValidationOrder validationOrder = determineGroupValidationOrder( groups );

        // 根据配置构建注解校验上下文
		ValidationContext<T> context = getValidationContext().forValidateParameters(
				parameterNameProvider,
				object,
				executable,
				parameterValues
		);

		if ( !beanMetaDataManager.isConstrained( context.getRootBeanClass() ) ) {
			return Collections.emptySet();
		}

        // 参数校验的核心逻辑:从注解校验上下文中进行参数校验
		validateParametersInContext( context, parameterValues, validationOrder );
         
        // 校验结束后从注解校验上下文中获取失败约束校验信息
		return context.getFailingConstraints();
	}

validateParametersInContext中方法比较复杂,大概执行的逻辑是按照分组、参数、是否级联操作、是否遇到错误校验就直接结束(FailFast)等,此处只讲核心处理逻辑:

ConstraintTree.java---validateSingleConstraint

private <T, V> Set<ConstraintViolation<T>> validateSingleConstraint(ValidationContext<T> executionContext,
			ValueContext<?, ?> valueContext,
			ConstraintValidatorContextImpl constraintValidatorContext,
			ConstraintValidator<A, V> validator) {
		boolean isValid;
		try {
			@SuppressWarnings("unchecked")    
            // 获取方法上的参数的具体值
			V validatedValue = (V) valueContext.getCurrentValidatedValue();
            // 具体校验逻辑:示例中使用的是@Min,此处的validator实现类是MinValidatorForNumber
			isValid = validator.isValid( validatedValue, constraintValidatorContext );
		}
		// 省略部分代码...........
		return Collections.emptySet();
	}

MinValidatorForNumber?.java实现参数值大小校验的逻辑:

public class MinValidatorForNumber implements ConstraintValidator<Min, Number> {

	private long minValue;

	public void initialize(Min minValue) {
		this.minValue = minValue.value();
	}
        
    // @Min参数校验的具体逻辑,先判断数据类型然后比较大小.
	public boolean isValid(Number value, ConstraintValidatorContext constraintValidatorContext) {
		// null values are valid
		if ( value == null ) {
			return true;
		}

		//handling of NaN, positive infinity and negative infinity
		else if ( value instanceof Double ) {
			if ( (Double) value == Double.POSITIVE_INFINITY ) {
				return true;
			}
			else if ( Double.isNaN( (Double) value ) || (Double) value == Double.NEGATIVE_INFINITY ) {
				return false;
			}
		}
		else if ( value instanceof Float ) {
			if ( (Float) value == Float.POSITIVE_INFINITY ) {
				return true;
			}
			else if ( Float.isNaN( (Float) value ) || (Float) value == Float.NEGATIVE_INFINITY ) {
				return false;
			}
		}

		if ( value instanceof BigDecimal ) {
			return ( (BigDecimal) value ).compareTo( BigDecimal.valueOf( minValue ) ) != -1;
		}
		else if ( value instanceof BigInteger ) {
			return ( (BigInteger) value ).compareTo( BigInteger.valueOf( minValue ) ) != -1;
		}
		else {
			long longValue = value.longValue();
			return longValue >= minValue;
		}
	}
}

至此,参数校验逻辑结束,校验错误信息在此不做说明.

2.请求对象参数注解

springMVC中RequestResponseBodyMethodProcessor.java专门对请求响应体做参数处理.解析参数,具体单个参数校验逻辑同上.

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
	// 获取参数上的注解数组	
    Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) {
            // 获取校验注解数组(校验注解以valid开头或直接是@Validated注解)
			Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
			if (validationHints != null) {
                // 绑定数据校验的具体逻辑,校验的具体实现:ConstraintTree.java中validateSingleConstraint
				binder.validate(validationHints);
				break;
			}
		}
	}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-13 09:08:16  更:2021-09-13 09:10:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:49:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码