概述
MethodValidationInterceptor是Spring Context提供的一个MethodInterceptor实现,它使用一个指定的JSR-303验证器对使用了相应JSR-303验证注解的方法参数或者返回值做验证。例子 :
// 注意,同时所属类上要使用 Spring 注解 @Validated
public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)
该例子约定如下验证逻辑 :
- 参数
arg1必须不能为null; - 参数
arg2最大值为10; - 方法
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]);
}
}
Spring MethodValidationInterceptor解析

本文深入探讨了Spring框架中MethodValidationInterceptor的实现细节及其在JSR-303验证中的应用。介绍了如何使用该拦截器对方法参数及返回值进行验证,并解释了其与Spring注解@Validated的关系。
3417

被折叠的 条评论
为什么被折叠?



