zoukankan      html  css  js  c++  java
  • spring源码学习(七)-springAOP的使用和源码

    AOP是指在程序运行期间动态的将代码切入到指定方法的指定位置进行运行
     使用步骤:
    * 1.在配置类上添加@EnableAspectJAutoProxy注解;如果是XML配置方式,在配置文件中加上<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    * 2.定义业务逻辑类(就是实际的业务处理代码),
    * 3.定义一个切面(Aspects),并添加通知方法
    * @Before 前置通知
    * @After 后置通知
    * @AfterReturning 返回通知
    * @AfterThrowing 异常通知
    * @Aroud 环绕通知
    * 4.需要再定义一个切点
    * 5.将切面和业务逻辑类都加入到容器中,@Component注解
    AOP中的AnnotationAwareAspectJAutoProxyCreator是在注册后置处理器(registerBeanPostProcessors(beanFactory);)的时候,完成创建和注册的
     
    需要哪些注解:
    需要在配置类加上@EnableAspectJAutoProxy注解
    需要声明一个切面,切面加@Component和@Aspect注解
    在切面中声明切点和advice方法(before after around ...)
    原理(基于注解版的):
     
    1.首先要从一个注解说起:
    @EnableAspectJAutoProx,该注解会import一个AspectJAutoProxyRegistrar.class
    AspectJAutoProxyRegistrar中,会在beanDefinitionMap中注册一个bean AnnotationAwareAspectJAutoProxyCreator.class
    AnnotationAwareAspectJAutoProxyCreator
    AspectJAwareAdvisorAutoProxyCreator
    AbstractAdvisorAutoProxyCreator
    AbstractAutoProxyCreator、BeanFactoryAware
    SmartInstantiationAwareBeanPostProcessor
    所以,这个注解的作用是在spring初始化的时候,注入了一个bean的后置处理器,这个处理器就是用来处理AOP的
     
    2.回到spring初始化的流程中,在refresh方法中,在执行invokeBeanFactoryPostprocessor方法的时候,我们知道,invokeBeanFactoryPostProcessors()方法主要是完成了bean的扫描,将bean扫描到beanDefinitionMap中;之前有说过,spring扫描bean,大致分为了三种情况:
      1.@Component注解;
      2.@Import注解,import注解又分为了两种:ImportSelector和ImportBeanDefinitionRegistrar;
      3.@Bean;
      而AOP的注解就是利用了@Import来给spring中注册bean的后置处理器;
      AspectJAutoProxyRegistrar类是实现了ImportBeanDefinitionRegistrar接口,所以在扫描bean的时候,会按照ImportBeanDefinitionRegistrar来处理,将AnnotationAwareAspectJAutoProxyCreator注入到beanDefinitionMap中
        @EnableAspectJAutoProxy的核心代码是
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
     
    3.在执行registerBeanPostProcessors()方法的时候,会把所有的beanPostProcessor的实现类,add到beanFactory的一个List集合中beanPostProcessors,这个集合中在执行完registerBeanPostProcessor方法之后,存放了spring自己的以及程序员提供的bean后置处理器的实现类;在这里添加后置处理器到list的时候,会有一个优先级的区分,
      List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
      List<String> orderedPostProcessorNames = new ArrayList<>();
      List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        这三个list中,从上到下,分别存储的是
      实现了PriorityOrdered接口的后置处理器实现类;
      实现了Ordered接口的bean后置处理器实现类
      没有实现Ordered和PriorityOrdered接口的bean后置处理器实现类
      在把bean后置处理器添加大beanPostProcessors的时候,会按照从上到下的优先级进行添加
     
      这个方法中,把所有的beanPostProcessor添加到集合中,是因为在spring初始化实例化bean的时候,都是依赖beanPostProcessor的各个实现类来实现的;所以,放到list中,方便后面遍历
     
    4.说完了bean后置处理器的初始化之后,我们该说在什么时候,进行动态代理了;
      当然,在spring初始化一个bean的时候,会涉及到多个后置处理器的工作,就是我另外一篇博客里面提到的,spring声明周期中用到的九大后置处理器,除了最后一个销毁时用到的,其他的八个在初始化和实例化的时候都会用到,但是spring的AOP中最主要的,用来生成代理对象的是第八个org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization;所以,这里只说这一个后置处理器
      这里就要说到刚才第一步中说的,添加的后置处理器了 AnnotationAwareAspectJAutoProxyCreator
      在说之前,先说一下这个后置处理器中的postProcessBeforeInstantiation方法,是在spring整个生命周期中,用到的第一个后置处理器方法,只不过,在aop中,在AnnotationAwareAspectJAutoProxyCreator中,这个方法是用来判断哪些bean是一定不需要进行代理的,这里说的是,spring如果判断当前bean一定不需要代理,就会把bean放到一个map中,key是beanClass;并把value设置为false。advisedBeans;这个map在afterInitialization方法中有用到;这里是如何推断当前bean一定不需要动态代理呢?
      boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
              Pointcut.class.isAssignableFrom(beanClass) ||
              Advisor.class.isAssignableFrom(beanClass) ||
              AopInfrastructureBean.class.isAssignableFrom(beanClass);
    除了这几个判断条件,还会判断当前bean是否是切面(判断当前bean是否有@Aspect注解)
        return (super.isInfrastructureClass(beanClass) ||
              (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
      所以,在beforeInstantiation中,会把一定不需要生成动态代理的bean添加到advisedBeans中,并把value设置为false
     
     在实例化bean的时候,会调用后置处理器来判断当前bean是否需要增强,调用的是 AbstractAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator 继承了该抽象类)的postProcessAfterInitialization方法,判断当前bean是否需要增强;
      在判断是否需要增强的时候,会拿到所有的切面,以及切入规则;其中个人认为一个比较重要的方法是
         canApply(pca.getPointcut(), targetClass, hasIntroductions);
         pointCut的pointCutExpression会保存切点的匹配规则(就是execution里面的表达式)
         然后,spring会把当前bean中所有符合切点规则的通知方法,放到一个list里面,如果list大于0,就通过CGLIB或者jdk动态代理来生成代理对象
      
      
    1 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    //这里就是判断返回的是否是null,如果不是null,就生成代理对象
    2 if (specificInterceptors != DO_NOT_PROXY) { 3 this.advisedBeans.put(cacheKey, Boolean.TRUE); 4 Object proxy = createProxy( 5 bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); 6 this.proxyTypes.put(cacheKey, proxy.getClass()); 7 return proxy; 8 }
     
    5.执行目标方法:
      1.在执行目标方法的时候,由于我这里是CGLIB生成的代理对象,所以会调用到代理对象CglibAopProxy的intercept方法
      2.首先会根据目标方法,获取到当前方法的拦截器链(拦截器链就是把当前方法要用到的通知方法转换成了methodIntercept)
      3.如果拦截器链为空,就直接执行目标方法
      4.否则CglibMethodInvocation的proceed方法
      5.在执行proceed方法的时候,通过一个currentInterceptorindex来控制执行哪个通知方法;并通过一个递归调用,来控制执行顺序;关于这里的执行顺序,还是没怎么搞明白,需要后面再学习一下 
     
        这是最后执行通知方法的代码,就是在这个方法中,控制了通知方法的优先级
     1 public Object proceed() throws Throwable {
     2     //    We start with an index of -1 and increment early.
     3     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
     4         return invokeJoinpoint();
     5     }
     6 
     7     Object interceptorOrInterceptionAdvice =
     8             this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
     9     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    10         // Evaluate dynamic method matcher here: static part will already have
    11         // been evaluated and found to match.
    12         InterceptorAndDynamicMethodMatcher dm =
    13                 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    14         if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
    15             return dm.interceptor.invoke(this);
    16         }
    17         else {
    18             // Dynamic matching failed.
    19             // Skip this interceptor and invoke the next in the chain.
    20             return proceed();
    21         }
    22     }
    23     else {
    24         // It's an interceptor, so we just invoke it: The pointcut will have
    25         // been evaluated statically before this object was constructed.
    26         return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    27     }
    28 }
  • 相关阅读:
    如何下载无水印的抖音视频?
    @valid和自定义异常
    Centos7查看外网ip,yum安装的curl无法正常使用
    ElasticSearch安装
    Redis的主从架构+哨兵模式
    Redis的持久化方式
    Nacos 注册中心集群搭建
    kafka安装流程
    WinUI 3学习笔记(1)—— First Desktop App
    .NET 5学习笔记(12)——WinUI 3 Project Reunion 0.5
  • 原文地址:https://www.cnblogs.com/mpyn/p/12183429.html
Copyright © 2011-2022 走看看