Spring @Validated 失效分析

最近在落地 DDD,希望对 command 进行参数校验,由于部分流量入口是 MQ,所以希望在应用层是用 @Validated 进行参数校验,结果。。。

Controller 中使用 @Validated

@Validated 注解的作用这里就不多做介绍了,具体用法在网上应该有不少。

在之前使用 MVC 架构编码时,通常是将 @Validated 注解或者 @Valid 配置在 Controller 的方法中,如下代码所示:

@PostMapping("common/set")
public Response<?> setCommonSetting(@RequestBody @Validated SetCommonSettingReqVO reqVO) {
    //doSomeThings
    return Response.success();
}
复制代码

所以在配置应用层校验时,就想当然的按照类似的写法:

public void addClueTrack(@Validated AddClueTrackCommand command) {
    //doSomeThings
}
复制代码

结果可想而知,@Validated 注解并不生效。

@Validated 是怎么生效的?

竟然不生效,那么就开始分析原因。

首先可以很容易想到,竟然能在方法执行前就拦截进行校验,那么大概率是使用动态代理。就和 @Transactional 事务注解一样,底层都是基于 AOP 实现动态代理。

接下来为了印证这个想法,就是需要深入看看 Spring 实现的。通过 IDE 可以很方便看到有哪些地方引用了 @Validated 注解:

其中一个类名一下就引起了我的注意 MethodValidationPostProcessor,熟悉 Spring 的小伙伴应该知道,Spring 中有很多 BeanPostProcessor 用于扩展 Bean,Aop 便是基于此实现动态代理的。点进去一看,果不其然:

public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
        implements InitializingBean {

    private Class<? extends Annotation> validatedAnnotationType = Validated.class;

    @Nullable
    private Validator validator;

    //...

    @Override
    public void afterPropertiesSet() {
        //创建切点
        Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
        this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
    }

    protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
        //创建拦截器
        return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
    }
}

public class AnnotationMatchingPointcut implements Pointcut {

    private final ClassFilter classFilter;
    private final MethodMatcher methodMatcher;
    
    public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType, boolean checkInherited) {
        //切点只针对类级别
        this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);
        this.methodMatcher = MethodMatcher.TRUE;
    }

    //...
}
复制代码

MethodValidationPostProcessor 中创建了一个切点,过滤类上添加了 @Validated 的 Bean,只要满足此条件,就会根据 MethodValidationInterceptor 生成对应的代理类。嗯,和 @Transactional 的实现原理差不多。

ok,看到这里我就在应用服务实现上添加了 @Validated 注解,那么此时注解生效了吗?哈哈,进度条还没过半呢😂

理论上类上加上 @Validated 注解,应该会生成动态代理类的,竟然没成功进行参数校验,我能想到的原因有二:

1. MethodValidationPostProcessor 没注入到 BeanFactory 中,所以没生成对应的代理类 2. MethodValidationInterceptor 对还有其他需要满足的条件,而目前还未满足

这里先剧透一下,答案是 2 🌝

MethodValidationInterceptor 需要满足什么条件

竟然答案是2,那这里就先讲一下 MethodValidationInterceptor,MethodValidationPostProcessor 是怎么注册到容器的咱们后面再来讲。

ExecutableValidatorpublic class MethodValidationInterceptor implements MethodInterceptor {

    private final Validator validator;

    @Override
    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {

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

        //获取类本身的实例(非代理
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值