Spring method validation的实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wt_better/article/details/84635010

spring method validation实现原理研究

一言以蔽之:
spring validation方法级别校验的实现是通过MethodValidationPostProcessor类动态注册AOP切面,使用MethodValidationInterceptor对切点方法织入增强。

注意:学习本文必须要掌握:AOP的基本概念、拦截器的功能和原理、spring aware接口的功能和大致原理。
如果对以上知识的不太清楚的话,建议首先研究下这些。

具体实现流程如下所示:

1、 如下图所示,在MethodValidationPostProcessorafterPropertiesSet方法中,动态创建了一个AnnotationMatchingPointcut类型切点,同时调用createMethodValidationAdvice方法创建出对应的增强。

注意:afterPropertiesSet方法是InitializingBean接口中定义的方法,用于在spring容器初始化的时候,进行对Bean的一些初始化操作,调用的时机是在bean的属性都set完毕。

在这里插入图片描述
AnnotationMatchingPointcut,见文知意,即注解匹配类型的切点,MethodValidationPostProcessor会创建一个这样的切点。如下图,其中classAnnotationType接收到的值Validated.class,表明该切点匹配所有标注了Validated注解的类,同时checkInherited参数是true,表明即使子类不标注Validated注解,但是其父类或者接口标注之后,那么子类也会命中切点。
在这里插入图片描述
创建完切点之后,会创建增强,创建过程如下所示:
在这里插入图片描述
其中MethodValidationInterceptor是aop联盟MethodInterceptor接口的实现,MethodInterceptor接口中声明了invoke方法,在该方法实现中,可以织入对应的增强。

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {

	/**
	 * Implement this method to perform extra treatments before and
	 * after the invocation. Polite implementations would certainly
	 * like to invoke {@link Joinpoint#proceed()}.
	 * @param invocation the method invocation joinpoint
	 * @return the result of the call to {@link Joinpoint#proceed()};
	 * might be intercepted by the interceptor
	 * @throws Throwable if the interceptors or the target object
	 * throws an exception
	 */
	Object invoke(MethodInvocation invocation) throws Throwable;

}

其中MethodInterceptor织入增强的代码如下所示:
在这里插入图片描述

在这里调用Bean validation的检验接口
1、进行方法参数的校验

execVal.validateParameters(invocation.getThis(), 
    			methodToValidate,  invocation.getArguments(), groups);

2、进行方法返回值的校验

execVal.validateReturnValue(invocation.getThis(), 
						methodToValidate, returnValue, groups);

3、如果存在违反约束的情况,将会抛出ConstraintViolationException异常,我们可以使用ExceptionHandler显示捕获该异常,然后返回前端对应的message。

if (!result.isEmpty()) {
	throw new ConstraintViolationException(result);
}

捕获异常代码可以参考如下:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler
    public String handleException(Throwable e) {
        //方法级别校验异常,注意笔者这里使用快速失败机制,如果存在一条违反的约定,那么就不会继续后续的校验,所以这里返回的ConstraintViolations只有一条
        //读者可以根据自己的需求,按需返回对应的错误信息
        if (e instanceof ConstraintViolationException) {
            return ((ConstraintViolationException) e).getConstraintViolations().iterator().next().getMessage();
        }

        //对象级别校验,绑定异常
        if (e instanceof BindException) {
            return e.getMessage();
        }

        return e.getMessage();
    }
}

猜你喜欢

转载自blog.csdn.net/wt_better/article/details/84635010