likes
comments
collection
share

Spring 源码之 AOP 剖析

作者站长头像
站长
· 阅读数 28

源码环境

IDEA:2022.2.2

Spring 版本:5.2.x

备注:本文将从源码角度,分析 AOP 的实现过程。

AOP 前置知识点

最重要的几个概念

  • JoinPoint(连接点):被拦截到的程序执行点
  • PointCut(切入点):对连接点拦截的条件定义,何处拦截
  • Advice(通知):拦截到连接点后执行的代码
  • Aspect(切面):切点+通知
Spring AOP 流程

代理对象生成

  1. 某一个对象执行 bpp.after() 先准备好所有增强器:遍历所有bd,有Aspect注解,遍历所有方法生成InstantiationModelAwarePointcutAdvisorImpl对象
  2. 对象执行到bpp.after(),获取增强器列表,判断是否此类需要增强,有的话,列表增加一个ExposeInvocationInterceptor排序生成代理

方法执行

调用到:DynamicAdvisedInterceptor.intercept()

  1. 创建CglibMethodInvocation对象,调用processed,包含了增强器链数组
  2. 调用父类ReflectiveMethodInvocation processed方法
  3. 递归调用,从-1 开始遍历所有增强器
  4. 先取到第一个ExposeInvocationInterceptor,保存MethodInvocation,并获取下一个增强器链
  5. 递归获取增强器,直到执行完成
源码分析

示例

// 普通类,对此类拦截扩展
@Component
public class A {
  public void run(){
    System.out.println("A:run()");
  }
}
// 切面
@Aspect
@Component
public class CAspect {
  @Before("execution(* com.wl.anination.aop.A.*(..))")
  public void before(JoinPoint jp){
    System.out.println("before:"+jp.getSignature().getName());
  }
  @After("execution(* com.wl.anination.aop.A.*(..))")
  public void after(JoinPoint jp){
    System.out.println("after:"+jp.getSignature().getName());
  }
}
// 配置类
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.wl.anination.aop")
public class Config {
}

项目结构及结果

Spring 源码之 AOP 剖析

分析

BeanFactory:容器准备;1、加载config配置类 2、添加BFPP,后面会详细讲这个的作用,以及涉及到SpringBoot自动装配原理。

此时beanFactory 并非 obtainFreshBeanFactory() 方法创建,而是在ConditionEvaluator创建ConditionContextImpl对象通过deduceBeanFactory方法创建;这块和ClassXmlApplicaitonContent的区别之处。

Spring 源码之 AOP 剖析

此时只看到 config(其他默认的,而切面:CAspect、普通类:A未加载) ,是咱们自己写的配置类

默认添加是在:AnnotationConfigUtils.registerAnnotationConfigProcessors();

internalConfigurationAnnotationProcessor:处理@configuration注解

internalAutowiredAnnotationProcessor:处理@Autowired注解

internalCommonAnnotationProcessor:处理JSR-250,如:@Resource @PostConstruct @PreDestory

internalEventListenerProcessor:处理 @EventListener注解

internalEventListenerFactory:用于生产ApplicationListener对象的EventListenerFactory对象

invokeBeanFactoryPostProcessors:执行后置处理器,看Spring 源码之 Bean的创建(一)中 refresh-05,有对此方法的注释。

这块主要讲 前面注入的BFPP及ConfigurationClassPostProcessor,调用processConfigBeanDefinitions ,执行ConfigurationClassParser.doProcessConfigurationClass(),此方法会解析相应的注解

代码处理流程

@Component:这块Configuration继承Component,遇到configuration也会处理,会递归处理,因为内部类也可能是配置类

@PropertySource

@ComponentScan

@Import

@ImportSource

@Bean

处理接口默认实现,jdk8可以写接口默认实现,如果@Bean修饰应该也被执行

解析父类,如果配置类继承某个类,父类也会被解析

这块添加一个面试点

SpringBoot如何实现自动装配

启动类有@SpringBootApplication 注解,而此注解是复合注解包含@SpringBootConfiguration(包含@Configuration)、@EnableAutoConfiguration(@Import(AutoConfigurationImportSelector.class))

这块解析到 AutoConfigurationImportSelector.class,执行selectImports方法,解析 META-INF/spring.factories 文件,加载其中的类到容器,会继续解析这些类,而这些类就是自动装配的类

执行完后置处理器,多个三个类 @Component修饰的A,切面CASpect,还有一个internalAutoProxyCreator(配置类有EnableAspectJAutoProxy注解,解析时添加)

Spring 源码之 AOP 剖析 到此处响应的已经准备完成,下来就是创建对象过程

在代理对象前,应该先把相应的切面信息先创建好

Spring 源码之 AOP 剖析

需要创建的对象有9个,执行BPP.before 方法会先把需要的切面信息准备好

AbstractAutoProxyCreator.postProcessAfterInstantiation()

执行到wrapIfNecessary,

// isInfrastructureClass 判断bean的类型是不是内部的类型,比如Advice,Pointcut,Advisor,AopInfrastructureBean这些是跟AOP相关的,所以不应该被处理
// shouldSkip 是否跳过的类,第一执行这个方法(创建config),会准备切面相关信息
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

是否跳过不被代理

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

最终调用到BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors

获取容器中所有信息,判断有ASpect注解

获取所有方法,每个方法包装成InstantiationModelAwarePointcutAdvisorImpl对象

return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
      this, aspectInstanceFactory, declarationOrderInAspect, aspectName);

放到缓存中

Spring 源码之 AOP 剖析

准备好相关数据,下来对A代理

还是执行到 bpp.after() 方法调用wrapIfNecessary方法进行代理

首先获取 通知列表 advisors

protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

    // 找到合适的增强器
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
    }
    return advisors.toArray();
  }

增强器列表处理:是否满足,添加ExposeInvocationInterceptor

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   // 将当前系统中所有的切面类的切面逻辑进行封装
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 对获取到的所有Advisor进行判断,判断切面定义是否可以应用到当前bean
    // 这块主要是AOPUtils.canApply进行判断
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 用于对目标Advisor进行扩展:ExposeInvocationInterceptor,此扩展排序后在第一位
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
      // 对增强排序(拓扑排序)
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

判断是否被代理

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 切点表达式的匹配:ClassFilter和MethodMatcher
    // MethodMatcher中有两个matches方法。
    // 一个参数是只有Method对象和targetClass,用来匹配静态方法
    // 另一个参数有Method对象和targetClass对象还有一个Method的方法参数,用来匹配运行期
    
    // 首先类必须在规则下
    if (!pc.getClassFilter().matches(targetClass)) {
      return false;
    }

    // 再进行MethodMatcher方法级别的校验
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
      // No need to iterate the methods if we're matching any method anyway...
      return true;
    }

    // 判断匹配器是不是IntroductionAwareMethodMatcher
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    Set<Class<?>> classes = new LinkedHashSet<>();
    // 判断当前class是不是代理的class对象
    if (!Proxy.isProxyClass(targetClass)) {
      classes.add(ClassUtils.getUserClass(targetClass));
    }
    // 获取到targetClass所实现的接口的class对象,然后加入到集合中
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    // 循环所有的class对象
    for (Class<?> clazz : classes) {
      // 通过class获取到所有的方法循环
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
      for (Method method : methods) {
        // 只要有一个方法能匹配到就返回true
        // 所以在运行时进行方法拦截的时候还会有一次运行时的方法切点规则匹配
        if (introductionAwareMethodMatcher != null ?
            introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) 
            // 通过方法匹配器进行匹配
            methodMatcher.matches(method, targetClass)) {
          return true;
        }
      }
    }
    return false;
  }

Spring 源码之 AOP 剖析 下来进行代理

Object proxy = createProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      // 执行到此处
      // 判断是 使用jdk动态代理 还是cglib代理
      if (shouldProxyTargetClass(beanClass, beanName)) {
        proxyFactory.setProxyTargetClass(true);
      }
      else {
      // 添加代理接口
        evaluateProxyInterfaces(beanClass, proxyFactory);
      }

生成代理对象

Spring 源码之 AOP 剖析

方法执行:run

生成的类,查看run调用的是:var10000.intercept(this, CGLIBrunrunrun0Method,CGLIBMethod, CGLIBMethod,CGLIBemptyArgs, CGLIBrunrunrun0$Proxy);即:DynamicAdvisedInterceptor

Spring 源码之 AOP 剖析

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      Object oldProxy = null;
      boolean setProxyContext = false;
      Object target = null;
      TargetSource targetSource = this.advised.getTargetSource();
      try {
        if (this.advised.exposeProxy) {
          // Make invocation available if necessary.
          oldProxy = AopContext.setCurrentProxy(proxy);
          setProxyContext = true;
        }
        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
        // 从advised中获取配置好的AOP通知
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        // Check whether we only have one InvokerInterceptor: that is,
        // no real advice, but just reflective invocation of the target.
        // 如果没有aop通知配置,那么直接调用target对象的调用方法
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
          // We can skip creating a MethodInvocation: just invoke the target directly.
          // Note that the final invoker must be an InvokerInterceptor, so we know
          // it does nothing but a reflective operation on the target, and no hot
          // swapping or fancy proxying.
          Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
          // 如果拦截器链为空则直接激活原方法
          retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
          // We need to create a method invocation...
          // 通过cglibMethodInvocation来启动advice通知
          retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
      }
      finally {
        if (target != null && !targetSource.isStatic()) {
          targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
          // Restore old proxy.
          AopContext.setCurrentProxy(oldProxy);
        }
      }
    }

CglibMethodInvocation extends ReflectiveMethodInvocation

private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
 //…… 
    public Object proceed() throws Throwable {
      try {
        // 调用到ReflectiveMethodInvocation
        return super.proceed();
      }
      catch (RuntimeException ex) {
        throw ex;
      }
      catch (Exception ex) {
        if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
          throw ex;
        }
        else {
          throw new UndeclaredThrowableException(ex);
        }
      }
    }
  }

ReflectiveMethodInvocation

public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
   // 从 索引为-1 拦截器开始调用,调用完毕后调用target函数,通过反射
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
    }

    // 获取下一个要执行的拦截器,沿着定义好的interceptorOrInterceptionAdvice链进行处理
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      // 这里对拦截器进行动态匹配的判断,这里是对pointcut触发进行匹配的地方,如果和定义的pointcut匹配,那么这个advice将会得到执行
      InterceptorAndDynamicMethodMatcher dm =
          (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
        return dm.interceptor.invoke(this);
      }
      else {
        // Dynamic matching failed.
        // Skip this interceptor and invoke the next in the chain.
        // 如果不匹配,那么proceed会被递归调用,知道所有的拦截器都被运行过位置
        return proceed();
      }
    }
    else {
      // It's an interceptor, so we just invoke it: The pointcut will have
      // been evaluated statically before this object was constructed.
      // 普通拦截器,直接调用拦截器,将this作为参数传递以保证当前实例中调用链的执行
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
  }

调用链

  1. ExposeInvocationInterceptor

    作用:就是用来传递MethodInvocation的。 在后续的任何下调用链环节,只要需要用到当前的MethodInvocation就通过ExposeInvocationInterceptor.currentInvocation()静态方法获得

    public Object invoke(MethodInvocation mi) throws Throwable {
        MethodInvocation oldInvocation = invocation.get();
        invocation.set(mi);
        try {
          return mi.proceed();
        }
        finally {
          invocation.set(oldInvocation);
        }
      }
    
  2. MethodBeforeAdviceInterceptor

    public Object invoke(MethodInvocation mi) throws Throwable {
        // 执行前置通知的方法
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        // 执行下一个通知/拦截器,但是该拦截器是最后一个了,所以会调用目标方法
        return mi.proceed();
      }
    
  3. AspectJAfterAdvice

    public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			// 执行下一个通知/拦截器
    			return mi.proceed();
    		}
    		finally {
    			// 后置通知的方法总是会被执行,原因就在这finally
    			invokeAdviceMethod(getJoinPointMatch(), null, null);
    		}
    	}
    

    到此 AOP 代理创建及执行已经分析完。

    总结

    本文讲到AOP 代理创建及执行流程,顺便分析SpringBoot 自动装配原理。

    AOP 中调用链,用到了责任链模式,但又不同于责任链,没有next只想下一个待执行的对象,而是通过添加一个类 ExposeInvocationInterceptor (启到一个总调度的作用,把所有调用链串起来),通过递归执行完所有增强器。

转载自:https://juejin.cn/post/7372396174248820774
评论
请登录