它是个普通的BeanPostProcessor,为Bean创建的代理的时机是postProcessAfterInitialization(),也就是在Bean完成初始化后有必要的话用一个代理对象返回进而交给Spring容器管理~(同@Aysnc)
容易想到,关于校验方面的逻辑不在于它,而在于切面的通知:MethodValidationInterceptor
它是AOP联盟类型的通知,此处专门用于处理方法级别的数据校验。
注意理解方法级别:方法级别的入参有可能是各种平铺的参数、也可能是一个或者多个对象
// @since 3.1 因为它校验Method 所以它使用的是javax.validation.executable.ExecutableValidator public class MethodValidationInterceptor implements MethodInterceptor { // javax.validation.Validator private final Validator validator; // 如果没有指定校验器,那使用的就是默认的校验器 public MethodValidationInterceptor() { this(Validation.buildDefaultValidatorFactory()); } public MethodValidationInterceptor(ValidatorFactory validatorFactory) { this(validatorFactory.getValidator()); } public MethodValidationInterceptor(Validator validator) { this.validator = validator; } @Override @SuppressWarnings("unchecked") public Object invoke(MethodInvocation invocation) throws Throwable { // Avoid Validator invocation on FactoryBean.getObjectType/isSingleton // 如果是FactoryBean.getObject() 方法 就不要去校验了~ if (isFactoryBeanMetadataMethod(invocation.getMethod())) { return invocation.proceed(); } Class<?>[] groups = determineValidationGroups(invocation); // Standard Bean Validation 1.1 API ExecutableValidator是1.1提供的 ExecutableValidator execVal = this.validator.forExecutables(); Method methodToValidate = invocation.getMethod(); Set<ConstraintViolation<Object>> result; // 错误消息result 若存在最终都会ConstraintViolationException异常形式抛出 try { // 先校验方法入参 result = execVal.validateParameters(invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } catch (IllegalArgumentException ex) { // 此处回退了异步:找到bridged method方法再来一次 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 = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups); if (!result.isEmpty()) { throw new ConstraintViolationException(result); } return returnValue; } // 找到这个方法上面是否有标注@Validated注解 从里面拿到分组信息 // 备注:虽然代理只能标注在类上,但是分组可以标注在类上和方法上哦~~~~ 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]); } }这个Advice的实现,简单到不能再简单了,稍微有点基础的应该都能很容易看懂吧(据我不完全估计这个应该是最简单的)。
==使用细节==(重要)