spring源码研读-AOP-三步走?


本文将基于java-config(注解)研读 spring-AOP 源码

AOP的使用

  1. 配置类上添加@EnableAspectJAutoProxy注解 [开启AOP功能]

  2. AOP bean对象添加@Aspect注解

    @Aspect
    @Component
    public class AOPTest {
         @Pointcut("execution(* com.zhouxh.service.componentTest.*(..))")
         private void pointCut(){
    
         }
         @Before(value = "pointCut()")
         public void before(){
             System.out.println("aop Before start");
         }
    
         @After(value = "pointCut()")
         public void after(){
             System.out.println("aop After start");
         }
    
         @Around(value = "pointCut()")
         public void around(ProceedingJoinPoint joinPoint) throws Throwable {
             System.out.println("aop Around start");
             joinPoint.proceed();
             System.out.println("aop Around end");
         }
         @AfterReturning(value = "pointCut()")
         public void afterReturning(){
             System.out.println("aop AfterReturning start");
         }
         @AfterThrowing(value = "pointCut()")
         public void afterThrowing(){
             System.out.println("aop AfterThrowing start");
         }
    }
    

1. 加载切面

  1. 我们再开启AOP功能时,在配置类上添加了@EnableAspectJAutoProxy注解。该注解在配置类被解析时,会在spring容器中添加一个AnnotationAwareAspectJAutoProxyCreator后置处理器。

    @EnableAspectJAutoProxy
    

    进入该注解,我们可以看到该注解向spring 容器中注入了AspectJAutoProxyRegistrar

    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    

    进入AspectJAutoProxyRegistrar类,只有一个构造函数。
    这个函数做了两件事情:

    • 在spring中注入了AnnotationAwareAspectJAutoProxyCreator后置处理器
    • 判断是否设置proxyTargetClass 和 exposeProxy属性
      • proxyTargetClass 属性,默认为false,如果为true时,表示相关bean对象动态代理强制使用CGLIB
      • exposeProxy属性,默认为false,如果为true,有jdk动态代理生成bean对象出现方法内部调用时,也会触发AOP功能。
        public void registerBeanDefinitions(
        AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 在spring中注入了AnnotationAwareAspectJAutoProxyCreator后置处理器
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        // 获取注解@EnableAspectJAutoProxy
        AnnotationAttributes enableAspectJAutoProxy = 
              AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
             // 判断是否设置proxyTargetClass 和 exposeProxy属性
          if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
              AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
          }
          if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
              AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
          }
        }
        }
        
  2. 《spring IOC源码解析》这篇文章中,我们知道spring_bean第一次初始化会调用createBean()方法创建一个bean实例。在bean被创建【doCreateBean方法】前,通过spring提供的拓展点【resolveBeforeInstantiation方法】,将切点和通知包装成Advisors

    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
    
    /**
     * 省略部分不相干代码
     */
    try {
        // spring 容器提供的拓展点,会在此处运行spring前置处理器
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }catch (Throwable ex) {}
    
    try {
        // 创建一个bean实例
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
        // 省略
    }catch (Throwable ex) {
        // 省略
    }
    

    进入 resolveBeforeInstantiation方法,最终调用 applyBeanPostProcessorsBeforeInstantiation 方法循环调用所有的前置处理器。

    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }
    

    也就是在这里调用了我们在@EnableAspectJAutoProxy注解,给spring容器中添加的AnnotationAwareAspectJAutoProxyCreator后置处理器。

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        Object cacheKey = getCacheKey(beanClass, beanName);
    
        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            // isInfrastructureClass 判断当前beanClass是否被@Aspectj注释,是 返回true,否则返回false
            // AOP 类会被添加到缓存中,然后 保存在advisedBeans缓存中
            // shouldSkip()方法解析各通知,并与切点一起包装成Advisors
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
    
        省略了部分代码
    ```
     return null;
    

    }

    `shouldSkip()`方法解析各通知,并与切点一起包装成`Advisors`。
    ``` java
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // TODO: 查询所有的通知并添加到缓存中,方便调用时获取。
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        for (Advisor advisor : candidateAdvisors) {
            // 判断AOP是否是通过xml配置的,如果是返回true 【为了兼容xml】
            if (advisor instanceof AspectJPointcutAdvisor &&
                    ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
                return true;
            }
        }
        return super.shouldSkip(beanClass, beanName);
    }
    

    findCandidateAdvisors()方法查询所有的通知并添加到缓存中,方便调用时获取。

    protected List<Advisor> findCandidateAdvisors() {
        // 查找所有继承了 Advisor.class的类【我们通过注解的形式,所以此处查询为空】
        List<Advisor> advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        if (this.aspectJAdvisorsBuilder != null) {
            // 真正生效的是 buildAspectJAdvisors() 解析变创建 advisors
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }
    

    通过aspectJAdvisorsBuilder.buildAspectJAdvisors()创建advisors并添加到缓存中。

    • this.advisorFactory.getAdvisors(factory)通过 解析切面类中方法上的注解 获取通知切点一起封装到advisor中,将所有advisors根据 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 的顺序进行排序。
    • 并将adviors保存在advisorsCache缓存中
      public List<Advisor> buildAspectJAdvisors() {
      ···
      此处省略部分判断代码
         ···
      // 通过解析class类中的方法上的注解 根据 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 排序
      List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
      if (this.beanFactory.isSingleton(beanName)) {
          // 将通知和切点 与 当前bean 一起保存在advisorsCache 缓存中
          this.advisorsCache.put(beanName, classAdvisors);
      }
      ···
         此处省略部分代码
         ···
      }
      

2. 创建代理

spring_bean被创建大致可以分为三步:1. bean的实例化;2. bean属性的注入;3. bean的初始化

AOP 创建动态代理在第三步:bean的初始化【exposedObject = initializeBean(beanName, exposedObject, mbd);

在bean的初始化完成之后,spring提供了一个调用后置处理器的拓展点,就在此处spring会调用AnnotationAwareAspectJAutoProxyCreator后置处理器的后置处理方法。

让我们来追溯源码!

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    ···
    省略部分代码:完成spring_bean的初始化工作
    ···
    // 在这之前,bean的初始化已经完成。
    // spring提供的拓展点,调用后置处理器的后置处理方法
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

applyBeanPostProcessorsAfterInitialization方法中循环所有后置处理器并调用对应的后置处理方法。

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

getBeanPostProcessors()获取所有的后置处理器,其中就有 AnnotationAwareAspectJAutoProxyCreator

通过调用父类AbstractAutoProxyCreator.postProcessAfterInitialization()方法 —> 最终调用wrapIfNecessary()方法生成动态代理。

 public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    // 不需要AOP增强的类,直接返回原始对象,不需要动态代理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    // 再次判断是否需要增强,不需要的直接返回
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 如果需要AOP 增强,创建动态代理
       // 通过 AspectJ 的 api 判断当前类中哪些方法符合哪些通知【通过切点表达式,选择需要增强的内容】
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
         // 创建动态代理 
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

看到这里我们还有一个疑问:AOP动态代理是怎么选择JDK动态代理 还是 CGLIB动态代理的?

继续跟进createProxy()方法,最终进入DefaultAopProxyFactory.createAopProxy()方法

  • isProxyTargetClass() —-> 我们在@EnableAspectJAutoProxy()注解中设置 proxyTargetClass = true;
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    
  • hasNoUserSuppliedProxyInterfaces() —-> true 表示 当前增强类没有实现任何接口
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
      if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
          Class<?> targetClass = config.getTargetClass();
          if (targetClass == null) {
              throw new AopConfigException("TargetSource cannot determine target class: " +
                      "Either an interface or a target is required for proxy creation.");
          }
          // 如果目标增强类,是一个接口 或者 是一个代理类 也会使用JDK动态代理
          if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
              return new JdkDynamicAopProxy(config);
          }
          return new ObjenesisCglibAopProxy(config);
      }
      else {
          return new JdkDynamicAopProxy(config);
      }
    }
    

看完这部分代码我们也能得出上面问题的答案了

  1. 如果在开启AOP的注解@EnableAspectJAutoProxy(proxyTargetClass = true)中添加了proxyTargetClass = true,那么spring会使用CGLIB动态代理。
  2. 如果目标增强类没有继承任何接口,那么spring会使用CGLIB动态代理。
  3. 如果目标增强类,是一个接口或者是一个代理类,也会使用JDK动态代理
  4. 否则,会使用JDK动态代理

    3. 调用代理

    我们知道被代理类的方法被执行时,会先调用代理类的代理方法【jdk动态代理的invoke()/CGLIB动态代理的intercept()】。

我们将以CGLIB动态代理为例,研读Spring AOP是如果调用代理的。

当被增强类的方法被执行时,spring会先调用CglibAopProxy的内部类DynamicAdvisedInterceptor.intercept()方法

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) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
        // 加载切面时,封装好的adviors集合
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        // 如果没有封装好的adviors,直接调用对应方法
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
            // AOP 执行通知的入口
            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);
        }
    }
}

以上代码核心在于:retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); 该方法是 AOP 执行通知的入口。

public Object proceed() throws Throwable {
    // currentInterceptorIndex 初始化时为 -1
    // 如果执行到AOP中声明的最后一个通知,调用目标方法。
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 调用目标增强方法
        return invokeJoinpoint();
    }
    // interceptorsAndDynamicMethodMatchers 包含所有的通知的集合,每次获取下一个通知
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {    
    // 省略部分代码
    }else {
        // 在此处调用对应通知的对应类
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

invoke()方法会更根据下图顺序依次调用。
interceptorsAndDynamicMethodMatchers中的对象

调用时序图

环绕通知类 - AspectJAroundAdvice

该类没有什么特殊的方法,就是调用AOP中被@Around注释的方法

public Object invoke(MethodInvocation mi) throws Throwable {
    if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    }
    ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
    JoinPointMatch jpm = getJoinPointMatch(pmi);
       // 调用环绕通知方法
    return invokeAdviceMethod(pjp, jpm, null, null);
}

在环绕通知方法中,先进行前置通知,然后唤醒一个通知,最后进行后置通知。

@Around(value = "pointCut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("aop Around start");
    joinPoint.proceed();
    System.out.println("aop Around end");
}

前置通知 - MethodBeforeAdviceInterceptor

先调用AOP中被@Before注释的方法,然后唤醒下一通知

public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}

后置通知 - AspectJAfterAdvice

先唤醒下一通知,再调用AOP中被@After注释的方法。

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    finally {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

后置返回通知 - AfterReturningAdviceInterceptor

唤醒下一通知,再调用AOP中被@AfterReturning注释的方法。

public Object invoke(MethodInvocation mi) throws Throwable {
    Object retVal = mi.proceed();
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}

后置异常通知 - AspectJAfterThrowingAdvice

先执行下一通知,如果出现异常,再执行@AfterThrowing注释的方法。

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}

在唤醒下一通知前,会判断是否存在下一通知,如果不存在,直接调用目标增强方法。

该判断在ReflectiveMethodInvocation.proceed()方法中

public Object proceed() throws Throwable {
    // currentInterceptorIndex 初始化时为 -1
    // 如果执行到AOP中声明的最后一个通知,调用目标方法。
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 调用目标增强方法
        return invokeJoinpoint();
    }
    ····
    ····
}

总结

  1. spring会在doCreateBean之前,会调用 spring 提供的拓展点【resolveBeforeInstantiation】,从所有 beanDefinitionMap 中获取所有的 Object 类型的类,循环判断并获取哪个类被 @Aspectj 注解注释。然后解析该类中被 @Before、@After、@Around、@AfterReturning、@AfterThrowing 注解注释的方法,并与 @Pointcut 注释的切点一起封装在 adviors 中。

  2. 在 spring 初始化方法中,调用所有后置处理器的后置处理方法,获取所有的 adviors 和当前类进行匹配【匹配方法是调用AspectJ的内部API】。匹配成功的类再由 AOPProxyFactory 根据【是否有接口、是否有proxyTargetClass = true】选择使用CGLIB动态代理还是JDK动态代理。

  3. 在目标增强方法被执行时,spring 会将adviors转换成Interceptor,然后通过责任链的模式一次进行调用。


文章作者: zhouxh-z
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zhouxh-z !
 上一篇
消息队列[1]--RabbitMQ 消息队列[1]--RabbitMQ
什么是消息队列 消息:请求或应用间传输的数据 队列:一种先进先出的数据结构 消息队列:字面意思就是一种存放数据的先进先出的数据结构或者说容器 总的来说:消息队列是一种服务间 异步通信组件 ,主要解决应用解耦,异步处理,流量削锋等问题,实现
下一篇 
一文解读 zookeeper 一文解读 zookeeper
zookeeper 是什么?官方文档是这样解读zookeeper的:它是一个分布式协调框架,是Apache Hadoop 的一个子项目,它主要是用来 解决分布式应用中 经常遇到的一些 数据管理问题,如:统一命名服务、状态同步服务、集群管理、
  目录