• SpringAOP的源码解析


    一、SpringAOP的概念

    一、AOP的基本概念

    1、连接点(Joinpoint):可以被增强的方法。

    2、切点(Pointcut):实际被增强的方法。

    3、通知(Advice)(增强):

      3.1.实际增强的逻辑部分叫做通知

      3.2.通知类型包括

    1. 前置通知(执行方法前执行,通常用作参数日志输出、权限校验等)
    2. 后置通知(逻辑代码执行完,准备执行return的代码时通知,通常用作执行结果日志输出、结果加密等)
    3. 环绕通知(是前置通知和后置通知的综合,方法执行前和方法执行后都要执行,通常用作方法性能统计、接口耗时、统一加密、解密等)
    4. 异常通知(相当于try{}catch ()中catch执行的部分,程序抛出异常时执行,通常用作告警处理、事务回滚等)
    5. 最终通知(相当于try{}catch (Exception e){}finally { }中的finally执行的部分,通常用在关闭资源、清理缓存等业务逻辑中)

    4、切面(Aspect):把通知(增强)应用到切入点的过程。

    二、Spring 框架一般都是基于 AspectJ 实现 AOP 操作

    (1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作

    三、基于 AspectJ 实现 AOP 操作

    (1)基于 xml 配置文件实现

    (2)基于注解方式实现(使用)

    二、SpringAOP的使用

    1.通过maven方式引用jar包

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>5.3.17</version>
        </dependency>
    

    2.创建被代理接口和实现类。代码如下:

    package com.ybe.aop;
    
    public interface  Calculate {
        /**
         * 除法
         * @param numA
         * @param numB
         * @return
         */
        int div(int numA,int numB);
    }
    
    
    package com.ybe.aop.impl;
    
    import com.ybe.aop.Calculate;
    
    public class CalculateImpl implements Calculate {
      
        @Override
        public int div(int numA, int numB) {
            System.out.println("执行目标方法:div");
            return numA / numB;
        }
    }
    
    

    3.使用@Aspect注解创建切面类(Aspect),代码如下:

    package com.ybe.aop.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    
    @Aspect
    @Component
    public class LogAspectj {
    
        @Pointcut("execution(* com.ybe.aop.impl.CalculateImpl.*(..))")
        private void pointCut(){
        }
    
        @Before(value = "pointCut()")
        public void methodBefore(JoinPoint joinPoint) throws Throwable {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("执行目标方法【"+methodName+"】的<前置通知>,入参"+Arrays.asList(joinPoint.getArgs()));
        }
    
        @After(value = "pointCut()")
        public void methodAfter(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("执行目标方法【"+methodName+"】的<后置通知>,入参"+Arrays.asList(joinPoint.getArgs()));
        }
    
        @AfterReturning(value = "pointCut()",returning = "result")
        public void methodReturning(JoinPoint joinPoint, Object result) {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("执行目标方法【"+methodName+"】的<返回通知>,入参"+Arrays.asList(joinPoint.getArgs()));
        }
    
        @AfterThrowing(value = "pointCut()")
        public void methodAfterThrowing(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("执行目标方法【"+methodName+"】的<异常通知>,入参"+Arrays.asList(joinPoint.getArgs()));
        }
    
    }
    
    

    4.创建Config 配置类,使用注解@EnableAspectJAutoProxy开启aop功能,代码如下:

    package com.ybe.aop.config;
    
    import com.ybe.aop.Calculate;
    import com.ybe.aop.impl.CalculateImpl;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration
    @ComponentScan("com.ybe.aop")
    @EnableAspectJAutoProxy
    public class Config {
        @Bean
        public Calculate calculate(){
            return new CalculateImpl();
        }
    }
    

    5.Main的代码

     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
     Calculate proxyFactoryBean = context.getBean("calculate", Calculate.class);
     context.div(1,1);
    

    6.运行结果如下:

    三、SpringAOP的源码分析

    SpringAop实现利用了SpringIoc容器。在SpringIOC容器的生命周期过程中整合了SpringAOP的功能。大概过程:通过 @Import注册 实现了ImportBeanDefinitionRegistrar 接口的 AspectJAutoProxyRegistrar 类。在该类中添加实现了 InstantiationAwareBeanPostProcessor 接口的 AnnotationAwareAspectJAutoProxyCreator 类。在创建AnnoteationConfigApplicationContext的构造函数中会调用refresh()方法。refresh方法会进行 AspectJAutoProxyRegistrar 的调用,并且生成

    AnnotationAwareAspectJAutoProxyCreator 的Bean对象。在第一次调用 CreateBean 的时候,进行Advisors的创建。在创建完 Bean后会调用AnnotationAwareAspectJAutoProxyCreator的 postProcessAfterInitialization方法。从 Advisors 中查找是否匹配当前正在创建的Bean。如果能匹配,则创建相关的动态代理对象。

    完整源码分析分三部分:SpringAOP的初始化、创建动态代理、代理方法调用过程。

    一、SpringAOP的初始化。

    主要逻辑是找到所有标注了 @Aspect 的类,并且解析类中所有的通知方法并添加到 BeanFactoryAspectJAdvisorsBuilder.advisorsCache 缓存中。

    整体代码流程图如下:

    说明:

    1. 创建 AnnotationConfigApplicationContext() 容器。

    2. 在invokeBeanFactoryPostProcessors()中,会调用 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry() 。在此方法中,会找到 @EnableAspectJAutoProxy 的 @Import 属性传入的 AspectJAutoProxyRegistrar.class 类。并且执行该类的registerBeanDefinitions() 方法,创建类型为 AnnotationAwareAspectJAutoProxyCreator 、名称为org.springframework.aop.
      config.internalAutoProxyCreator的 RootBeanDefinition注册到BeanDefinitionRegistry中。

    3. 在 registerBeanPostProcessors() 中会根据上面一步生成的 RootBeanDefinition对象创建 AnnotationAwareAspectJAutoProxyCreator 的实例。

    4. 在 finishBeanFactoryInitialization() 中第一次执行到 AbstractAutowireCapableBeanFactory.createBean() 时,会执行一段这样的代码,如下

      try {
              // 让 BeanPostProcessors 有机会返回一个代理而不是目标 bean 实例
              Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
              if (bean != null) {
                  return bean;
              }
      }
      
      @Nullable
      protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
      		Object bean = null;
      		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
      			// Make sure bean class is actually resolved at this point.
      			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      				Class<?> targetType = determineTargetType(beanName, mbd);
      				if (targetType != null) {
      					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
      					if (bean != null) {
      						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
      					}
      				}
      			}
      			mbd.beforeInstantiationResolved = (bean != null);
      		}
      		return bean;
      }
      
      @Nullable
      protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
      	    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                   Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
                   if (result != null) {
                     return result;
                   }
                 }
               return null;
      }
      

      以上代码会执行 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation() 方法。在该方法中会 执行 shouldSkip() 方法。代码如下:

      @Override
      protected boolean shouldSkip(Class<?> beanClass, String beanName) {
          // TODO: Consider optimization by caching the list of the aspect names
       // 找到所有候选的 Advisors
       List<Advisor> candidateAdvisors = findCandidateAdvisors();
       for (Advisor advisor : candidateAdvisors) {
           if (advisor instanceof AspectJPointcutAdvisor &&
               ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
               return true;
           }
       }
       return super.shouldSkip(beanClass, beanName);
      }
      

      在 findCandidateAdvisors 中具体会生成所有的 Advisors。

      @Override
      protected List<Advisor> findCandidateAdvisors() {
      		// 找到所有的 实现了 Advisor.class 接口的类,并且生成候选的 Advisors.
      		List<Advisor> advisors = super.findCandidateAdvisors();
      		// 创建所有的带了 @Aspect 特性的切面类 .
      		if (this.aspectJAdvisorsBuilder != null) {
      			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
      		}
      		return advisors;
      }
      

      aspectJAdvisorsBuilder.buildAspectJAdvisors() 是核心。方法里面的逻辑如下:

      1.获取容器里所有的beanNames.
      2.遍历 beanNames,根据beanName获取对应的beanType对象。
      3.判断beanType是否有@Aspect注解。
      4.如果有,调用getAdvisorMethods()通过反射获取该类型所有的 advisor 的 method 元数据。
      5.遍历 methods 调用 getAdvisor() 获取 Advisor 对象(InstantiationModelAwarePointcutAdvisorImpl)
      6.添加到 this.advisorsCache 中。
      

    ​ postProcessBeforeInstantiation方法会缓存所有的advisor,方法的最后返回 null。至此整个 SpringAOP的初始化完成。

    二、创建动态代理

    ​ 在创建Bean的生命周期的 initializeBean 方法中,会执行 AnnotationAwareAspectJAutoProxyCreator的 postProcessAfterInitialization方法。该方法会拿缓存BeanFactoryAspectJAdvisorsBuilder.advisorsCache 中所有advisor的pointCut去匹配正在创建的实例Bean的所有方法。如果 advisor 和 Bean 的某一个方法能匹配上,则把该advisor添加到 advisor的候选集合中。直到找出匹配Bean的所有Adsivors。最后根据Adsivor的候选集合和Bean类型创建动态代理对象ProxyFactory。

    整体代码流程图如下:

    说明:

    1.List排序后的顺序为:

    ExposeInvocationInterceptor

    Around

    Before

    After

    AfterReturning

    AfterThrowing

    2.动态代理的创建

    创建动态代理有两种方法,一种是 JDK ,一种是 CGLib 。

    1.如果目标类有实现接口的话,则是使用JDK的方式生成代理对象。

    2.配置了使用Cglib进行动态代理或者目标类没有实现接口,那么使用Cglib的方式创建代理对象。

    三、动态代理调用

    以 JdkDynamicAopProxy 为例,在调用方法的时候会直接调用 JdkDynamicAopProxy.invoke()方法,里面的大概逻辑如下:

    1.获取被代理的实现类;

    2.找出所有匹配被调用方法的 advisor,并且转成具体的通知拦截器 MethodInterceptor,返回通知拦截器链。转换代码如下:

    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    // 从Advisor中获取 Advice
    Advice advice = advisor.getAdvice();
    // 如果 advice 本身就实现了  MethodInterceptor 接口 ,则直接进行转换
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    // AfterReturningAdviceInterceptor MethodBeforeAdviceInterceptor  ThrowsAdviceInterceptor 
    // 这三种是通过适配器的方式进行转换 MethodInterceptor类型
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
    

    3.创建 ReflectiveMethodInvocation 对象(该对象中包括了 代理对象、被代理对象、执行的方法、方法参数、被代理对象的类型、通知拦截器链),执行该对象的proceed()方法,该方法中会进行通知拦截器链的递归调用,具体调用流程如下图。ReflectiveMethodInvocation 对象在通知拦截器链调用中作用很关键,有衔接各个拦截器的作用。

    代码流程如下图:

    说明:

    1.在proceed方法中,会先判断当前拦截器链的索引,如果索引等于最后一个那么则执行被代理类的方法。

    2.如果不是,那么先获取该通知拦截器并且执行该拦截器的 proceed 方法(方法接受 ReflectiveMethodInvocation 对象实例),每个通知拦截器中都会调用 ReflectiveMethodInvocation 对象实例 的proceed 方法。在这里会形成递归调用。

    3.通知拦截器的排序请看下图:

    4.五个通知拦截器的代码解释请看上面的代码流程图。

  • 相关阅读:
    jar -jar运行原理
    黑客之批处理编写
    FTP、FTPS与SFTP定义与联系
    算法篇:排序算法
    举例说明PyTorch函数torch.cat与torch.stack的区别
    逆向分析练习六(实战CrackMe01)
    【java学习】对象的创建和使用(14)
    LeetCode刷题——等差数列划分#413#Medium
    vue2.x封装svg组件并使用
    CentOS 7 离线安装nginx1.18
  • 原文地址:https://www.cnblogs.com/yuanbeier/p/16155353.html