Spring Context : MethodValidationInterceptor

概述

MethodValidationInterceptorSpring Context提供的一个MethodInterceptor实现,它使用一个指定的JSR-303验证器对使用了相应JSR-303验证注解的方法参数或者返回值做验证。例子 :

// 注意,同时所属类上要使用 Spring 注解 @Validated
public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)

该例子约定如下验证逻辑 :

  1. 参数arg1必须不能为null;
  2. 参数arg2最大值为10;
  3. 方法myValidMethod返回值不能是null;

另外,类级别使用的Spring注解@Validated可以定义验证组,该验证注解会应用到该类所有public方法上。缺省情况下,JSR-303会仅仅使用缺省验证组进行验证。

Spring 5.0开始,MethodValidationInterceptor需要的验证器必须是Bean Validation 1.1版本。

关于MethodValidationInterceptor的应用,可以参考:Spring BeanPostProcessor : MethodValidationPostProcessor,这篇文章中,MethodValidationPostProcessor会引入MethodValidationInterceptor。而进一步关于MethodValidationPostProcessor和所使用到的Validator,可以参考:Spring Boot 自动配置 : ValidationAutoConfiguration,对于一个Spring Boot应用,它会通过ValidationAutoConfiguration定义两个bean:一个是Validator,一个是MethodValidationPostProcessor

源代码

源代码版本 Spring Context 5.1.5.RELEASE

package org.springframework.validation.beanvalidation;

// 省略 import 行


public class MethodValidationInterceptor implements MethodInterceptor {

    // 要使用的 JSR-303 验证器,需要通过构造函数被外部指定
	private final Validator validator;


	/**
	 * Create a new MethodValidationInterceptor using a default JSR-303 validator underneath.
	 */
	public MethodValidationInterceptor() {
		this(Validation.buildDefaultValidatorFactory());
	}

	/**
	 * Create a new MethodValidationInterceptor using the given JSR-303 ValidatorFactory.
	 * @param validatorFactory the JSR-303 ValidatorFactory to use
	 */
	public MethodValidationInterceptor(ValidatorFactory validatorFactory) {
		this(validatorFactory.getValidator());
	}

	/**
	 * Create a new MethodValidationInterceptor using the given JSR-303 Validator.
	 * @param validator the JSR-303 Validator to use
	 */
	public MethodValidationInterceptor(Validator validator) {
		this.validator = validator;
	}


    // MethodInterceptor 接口约定的方法 , 逻辑实现主流程如下 :
    // 1. 执行方法参数验证逻辑
    // 2. 调用目标方法
    // 3. 执行方法返回值验证逻辑
	@Override
	@SuppressWarnings("unchecked")
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
		if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
			return invocation.proceed();
		}

		Class<?>[] groups = determineValidationGroups(invocation);

		// Standard Bean Validation 1.1 API
		ExecutableValidator execVal = this.validator.forExecutables();
		Method methodToValidate = invocation.getMethod();
		Set<ConstraintViolation<Object>> result;

       // 方法参数上的验证逻辑,验证结果保存在 result 中,如果验证失败, result 不为空,
       // 此时会抛出异常 ConstraintViolationException
		try {
			result = execVal.validateParameters(
					invocation.getThis(), 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(), invocation.getThis().getClass()));
			result = execVal.validateParameters(
					invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
		}
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}

       // 调用目标方法 
		Object returnValue = invocation.proceed();

       // 对方法执行的返回值进行验证 ,验证结果保存在 result 中,如果验证失败, result 不为空,
       // 此时会抛出异常 ConstraintViolationException
		result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups);
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}

       // 验证逻辑和目标方法执行都正常,这里返回目标方法的执行结果 
		return returnValue;
	}

	private boolean isFactoryBeanMetadataMethod(Method method) {
		Class<?> clazz = method.getDeclaringClass();

		// Call from interface-based proxy handle, allowing for an efficient check?
		if (clazz.isInterface()) {
			return ((clazz == FactoryBean.class || clazz == SmartFactoryBean.class) &&
					!method.getName().equals("getObject"));
		}

		// Call from CGLIB proxy handle, potentially implementing a FactoryBean method?
		Class<?> factoryBeanType = null;
		if (SmartFactoryBean.class.isAssignableFrom(clazz)) {
			factoryBeanType = SmartFactoryBean.class;
		}
		else if (FactoryBean.class.isAssignableFrom(clazz)) {
			factoryBeanType = FactoryBean.class;
		}
		return (factoryBeanType != null && !method.getName().equals("getObject") &&
				ClassUtils.hasMethod(factoryBeanType, method.getName(), method.getParameterTypes()));
	}

	/**
	 * Determine the validation groups to validate against for the given method invocation.
	 * <p>Default are the validation groups as specified in the {@link Validated} annotation
	 * on the containing target class of the method.
	 * @param invocation the current MethodInvocation
	 * @return the applicable validation groups as a Class array
	 */
	protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
		Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class);
		if (validatedAnn == null) {
			validatedAnn = AnnotationUtils.findAnnotation(invocation.getThis().getClass(), Validated.class);
		}
		return (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
	}

}

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值