zoukankan      html  css  js  c++  java
  • @validated注解实现

     

    https://www.jianshu.com/p/89a800eda155

     

     

     

     

     

    用法

    
    public interface UserService {
        public  UserModel get2( Integer uuid) ;
    }
    @Validated      //① 告诉MethodValidationPostProcessor此Bean需要开启方法级别验证支持
    @Component
    public class UserServiceImpl implement UserService  {
        public @NotNull UserModel get2(@NotNull @Min(value = 1) Integer uuid) { //②声明前置条件/后置条件
            //获取 User Model
            UserModel user = new UserModel(); //此处应该从数据库获取
            if(uuid > 100) {//方便后置添加的判断(此处假设传入的uuid>100 则返回null)
                return null;
            }
            return user;
        }
    }
    
    <!--注册方法验证的后处理器-->
    <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
    

    @validated和@valid不同点

    在spring项目中,@validated和@valid功能很类似,都可以在controller层开启数据校验功能。
    但是@validated和@valid又不尽相同。有以下不同点:

    1. 分组
    2. 注解地方,@Valid可以注解在成员属性(字段)上,但是@Validated不行
    3. 由于第2点的不同,将导致@Validated不能做嵌套校验
    4. @valid只能用在controller。@Validated可以用在其他被spring管理的类上。
      对于第4点的不同,体现了@validated注解其实又更实用的功能。那就是@validated可以用在普通bean的方法校验上。

    @validated的使用注意点

    1 @validated和@valid都可以用在controller层的参数前面,但这只能在controller层生效。
    2 @validated如果要开启方法验证。注解应该打在类上,而不是方法参数上。
    3 方法验证模式下,被jsr303标准的注解修饰的可以是方法参数也可以是返回值,类似如下
    public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)
    4 @validated不支持嵌套验证。所以jsr303标准的注解修饰的对象只能基本类型和包装类型。其他类型只能做到检测是否为空,
    对于对象里面的jsr303标准的注解修饰的属性,不支持验证。

    如何实现

    看MethodValidationPostProcessor类继承图谱


     
    clipboard.png

    实现了InitializingBean和BeanPostProcessor。所以我们重点看生命周期的2个节点方法。
    BeanPostProcessor.postProcessAfterInitialization
    InitializingBean.afterPropertiesSet
    根据生命周期的顺序,先执行afterPropertiesSet方法。

    MethodValidationPostProcessor.afterPropertiesSet
        ->Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);//创建了切入点
          this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));//创建了切面。
    

    然后再看postProcessAfterInitialization。实现是在MethodValidationPostProcessor的祖父类AbstractAdvisingBeanPostProcessor上

    AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization
        ->if (bean instanceof Advised)//如果被拦截的bean已经是代理类了。
            if (this.beforeExistingAdvisors)//并且beforeExistingAdvisors=true的时候,
                advised.addAdvisor(0, this.advisor);//把当前切面放到代理类切面的第一位。可以想到beforeExistingAdvisors参数的作用是为了保证验证切面优先于其他的切面。
                                                    //并且这里我们也能得知,代理类的切面可能不止1个,相当于代理类里面还存在拦截器链一样。//后文会去看这块的源码
            ->if (isEligible(bean, beanName))//如果被拦截的bean不是一个代理类。先校验一下这个类是否有资格添加上 validated的切面。原理就是扫描这个类上面是否有@validated注解
                ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
                proxyFactory.addAdvisor(this.advisor);
                return proxyFactory.getProxy(getProxyClassLoader());//创建代理。
                    ->createAopProxy().getProxy(classLoader);
                        ->ProxyCreatorSupport.createAopProxy
                            ->getAopProxyFactory().createAopProxy(this)
                                ->DefaultAopProxyFactory.createAopProxy
                                    ->if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {//如果该类有接口或者是代理类了,则直接使用jdk动态代理
                                           return new JdkDynamicAopProxy(config);}
                                       return new ObjenesisCglibAopProxy(config);//否则使用cglib动态代理
                        ->JdkDynamicAopProxy.getProxy
                            ->Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);//这一步找到目标类的所有接口。
                                                    //另外根据配置决定是否把Advised接口也加进去。加进去就意味着代理类也是Advised的实现类。那这一步显示是加上了Advised接口
                            ->Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);//看到调用jdk动态代理的api了。
    

    小结:

    • 1 什么时候创建的切面
      在afterPropertiesSet方法中创建切面。
      这里我就有一个疑问了,是不是实现InitializingBean的类都优先被加载呢?不然怎么提前把切面创建出来呢?

    • 2 怎么扫描注解的
      实现BeanPostProcessor的方法,可以拦截到所有bean

    • 3 什么时候给目标类做代理的
      也是在BeanPostProcessor.postProcessAfterInitialization方法中做的,拦截了bean之后,就检测是否类似打上了@validated注解,
      如果有就创建代理,

    • 4 如果目标类已经代理了,怎么办
      如果已经是代理了,就直接添加切面。如果想优先使用验证切面,则需要设置优先级为0.那么怎么设置优先级呢?
      直接把beforeExistingAdvisors属性设置为true即可。

    • 5 代理类的拦截器链是怎么实现的呢?
      根据jdk动态代理的知识,会动态的给UserService 类创建一个实现类(代理类)并实现了接口中的所有方法,当调用接口中的方法其实先经过代理类。
      代理类在把请求传递给InvocationHandler实例。估计InvocationHandler实现里面有维护着一个拦截器链,那么InvocationHandler是怎么设置的呢?
      接着看源码

    ->JdkDynamicAopProxy.getProxy
        ->Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);//看到调用jdk动态代理的api了。
                                                                    //通过this可以看到InvocationHandler实例其实就是JdkDynamicAopProxy类。
    JdkDynamicAopProxy.invoke(Object proxy, Method method, Object[] args)
        ->List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//原来InvocationHandler
            //保存着切面配置信息AdvisedSupport advised。advised里面存储了切面集合,下标代表了优先级。这个是什么时候传递进来的呢?
            //是在创建proxyFactory的时候,把配置信息传递给proxyFactory的 proxyFactory.addAdvisor(this.advisor)。这个时候验证切面就进到了proxyFactory中去了。
            //创建代理InvocationHandler,又把切面配置信息传递过去的。
            ->AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice
                ->List<Object> chain = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);//把advised对象向下传递
                    ->DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, 
     
     
    0人点赞
     
    Spring
     
     


    作者:6cc89d7ec09f
    链接:https://www.jianshu.com/p/89a800eda155
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    正则表达式判断手机号是否11位
    php-流程管理(发起流程和审核流程)
    php-流程管理(新建流程)(php+Ajax)
    php-人员权限管理(RBAC)
    状态压缩DP
    树形DP
    Broken Keyboard UVA 11988 数组实现链表
    Trees in a Wood UVA
    POJ1061 青蛙的约会 拓展欧几里得
    UVA10215The Largest/Smallest Box(小数精度)
  • 原文地址:https://www.cnblogs.com/zhoading/p/12624008.html
Copyright © 2011-2022 走看看