5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

盛開的太陽發表於2021-02-11

一. AOP切面原始碼分析

原始碼分析分為三部分

1. 解析切面

2. 建立動態代理

3. 呼叫


 

  • 原始碼的入口
原始碼分析的入口, 從註解開始:
元件的入口是一個註解, 比如啟用AOP的註解@EnableAspectJAutoProxy. 在註解的實現類裡面, 會有一個@Import(""). 這個@Import("")就是引入的原始碼實現類. 比如AOP的@Import(AspectJAutoProxyRegistrar.class)
通常, Spring要開啟某一個功能, 都會增加一個註解, 如果我們再想要看某一個功能的原始碼, 那麼就可以從他的註解跟進去看,在找到@Import("")就找到原始碼的入口了

原始碼分析的入口, AOP註解:

package com.lxl.www.aop;

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configurable
// 使用註解的方式引入AOP
@EnableAspectJAutoProxy
@ComponentScan("com.lxl.www.aop")
public class MainConfig {

}

引入AOP, 我們需要在配置檔案中增加@EnableAspectJAutoProxy代理. 那麼想要去掉AOP的引入, 只需要將這個註解註釋掉就可以了.  這個註解解釋整個AOP的入口. 

提示: 其他元件的引入也是類似的, 通常引入元件, 需要增加一個註解, 而整個功能的入口就在這個主機上面.

接下來, 進入到註解類

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;

}

 

這是, 我們看到EnableAspectJAutoProxy類增加了一個@Import註解類, 我們知道Import註解可以向IoC容器中增加一個bean.

 

下面進入到AspectJAutoProxyRegistrar類

 

package org.springframework.context.annotation;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * Register, escalate, and configure the AspectJ auto proxy creator based on the value
     * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
     * {@code @Configuration} class.
     */
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

 

我們看到, 使用ImportBeanDefinitionRegistrar註冊了一個BeanDefinition.

需要記住的是, 通常使用ImportBeanDefinitionRegistrar結合@Import可以向容器中註冊一個BeanDefinition.

如何註冊的呢? 看具體實現. 

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

 註冊名字是internalAutoProxyCreator的AnnotationAwareAspectJAutoProxyCreator

@Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            BeanDefinitionRegistry registry, @Nullable Object source) {

        /**
         * 註冊一個AnnotationAwareAspectJAutoProxyCreator型別的bean定義
         */
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

 

如上結構梳理如下: 

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 

我們看到,  註冊了類AnnotationAwareAspectJAutoProxyCreator型別的bean. 這是一個什麼樣的類呢? 我們來看一下類的結構. 這個類的繼承結構很龐大, 我們只看和本次內容相關的繼承結構

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

解析切面, 建立動態代理, 都是在bean的後置處理器中進行的, 下面對照著AOP的實現原理以及createBean(建立bean)的過程來看

 

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

上圖是bean載入過程中呼叫的9次後置處理器. 在建立bean之前呼叫了InstantiationAwareBeanPostProcessor後置處理器判斷是否需要為這個類建立AOP, 也就是解析切面的過程. 所以在AnnotationAwareAspectJAutoProxyCreator裡面實現了InstantiationAwareBeanPostProcessor後置處理器的介面. 重寫了postProcessBeforeInstantiation方法. 

在createBean的第三階段初始化之後, 要建立AOP的動態代理, 呼叫了BeanPostProcess後置處理器, AnnotationAwareAspectJAutoProxyCreator也實現了BeanPostProcess介面. 重寫了postProcessAfterInitialization. 

同時也需要處理AOP的迴圈依賴的問題, 處理迴圈依賴是在屬性賦值之前呼叫SmartInstantiationAwareBeanPostProcessor後置處理器, 然後重寫getEarlyBeanReference方法. 我們看到AnnotationAwareAspectJAutoProxyCreator也實現了SmartInstantiationAwareBeanPostProcessor介面. 並重寫getEarlyBeanReference方法.

 

1) AOP解析切面 

 

 通過上面的分析,我們知道了, 解析切面是在重寫了InstantiationAwareBeanPostProcessor後置處理器的postProcessBeforeInstantiation方法. 所以,我們要找到AnnotationAwareAspectJAutoProxyCreator重寫的postProcessBeforeInstantiation方法.

小貼士

如何找到呢? 在idea中使用快捷鍵ctrl + o, 找到當前類重寫的所有方法. 在搜尋postProcessBeforeInstantiation, 就可以找到了

 進入建立動態代理的bean的後置處理器, 這是解析切面的第一個入口

@Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        ......
    }

我們在postProcessBeforeInstantiation方法的入口處打一個斷點,  接下來看一下這個介面的呼叫鏈

 

 如上圖, 可以看出我們的入口是main方法, 然後呼叫了refresh()方法, 執行的是refresh()方法的finishBeanFactoryInitialization()方法, 然胡呼叫了doGetBean()下的createBean().然後呼叫的是resolveBeforeInstantiation的applyBeanPostProcessorsBeforeInstantiation方法,在這裡獲取到所有的bean的後置處理器, 判斷這個bean的後置處理器是否是InstantiationAwareBeanPostProcessor的一個例項. 如果是, 那麼就呼叫postProcessBeforeInstantiation()方法. 

@Nullable
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        /**
         * 獲取容器中所有的後置處理器
         * 這之前有一個註冊bean定義的方法, 已經註冊過了. 所以在這裡可以獲取到列表
         *
         * 9次bean的後置處理器, 都是一個類實現InstantiationAwareBeanPostProcessor類, 重寫postProcessBeforeInstantiation方法
         */
        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;
    }

 

下面就來分析postProcessBeforeInstantiation()方法

@Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        /**
         * 在第一個bean建立的時候, 就會去呼叫所有的bean的後置處理器, 並且解析所有的切面.
         * 這一步是非常消耗效能的. 所以, 會放到快取當中
         */
        // 構建快取的key
        Object cacheKey = getCacheKey(beanClass, beanName);

        // 沒有beanName或者不包含在targetSourcedBeans
        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            // 判斷是否已經被解析過?
            if (this.advisedBeans.containsKey(cacheKey)) {
                // 解析過, 則直接返回
                return null;
            }
            /*
             * 判斷當前這個類是不是需要跳過的類.如果是基礎類或者是應該跳過裡的類, 則返回null, 表示這個類不需要被解析
             *
             * 判斷是不是基礎bean(是不是切面類, 通知, 切點). 因為如果類本身是一個通知, 切面, 那我們不需要解析它
             * 跳過的類: 預設是false. 在shouldSkip裡面拿到所有的bean定義, 標記是不是@Aspect, 然後將每一個通知生成一個advisor
             */
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                /**
                 * advisedBean是一個集合, 用來儲存類是否是一個advise
                 */
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        // Create proxy here if we have a custom TargetSource.
        // Suppresses unnecessary default instantiation of the target bean:
        // The TargetSource will handle target instances in a custom fashion.
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);

            // 建立了代理
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        return null;
    }

第一步: 構建快取

構建快取的key
Object cacheKey = getCacheKey(beanClass, beanName);
在第一個bean建立的時候, 就會去呼叫所有的bean的後置處理器, 並且解析所有的切面.
這一步是非常消耗效能的. 所以, 會放到快取當中. 已經建立過的,後面將不再建立

第二步: 校驗bean是否被解析過. 如果已經解析過, 則不再解析

// 判斷是否已經被解析過
if (this.advisedBeans.containsKey(cacheKey)) {
    // 解析過, 則直接返回
    return null;
}  

第三步: 判斷類是否是需要跳過的類

if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
    /**
     * advisedBean是一個集合, 用來儲存類是否是一個advise
     */
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return null;
}

 如果是基礎類或者是應該跳過的類, 則返回null, 表示這個類不需要被解析.

 這裡有兩個判斷.

isInfrastructureClass(beanClass) 判斷當前這個類是不是基礎類, 這裡的基礎類的含義如下: Advice、Pointcut、Advisor、AopInfrastructureBean。如果本身就是基礎類,那麼不用在解析了

protected boolean isInfrastructureClass(Class<?> beanClass) {
        // 如果這個類是一個Advice型別的類, 或者 Pointcut型別的類, 或者Adivsor型別的類, 或者AOPInsfrastructureBean型別的類.
        boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
                Pointcut.class.isAssignableFrom(beanClass) ||
                Advisor.class.isAssignableFrom(beanClass) ||
                AopInfrastructureBean.class.isAssignableFrom(beanClass);
        if (retVal && logger.isTraceEnabled()) {
            logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
        }
        return retVal;
    }

 

shouldSkip(beanClass, beanName)判斷當前是否是需要跳過的類 .

@Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // 找到候選的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(); 找到候選的類, 然後將候選類構造成Advisor物件. 進到方法裡看看是如何篩選出候選物件的. 

AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()

@Override
    protected List<Advisor> findCandidateAdvisors() {
        // Add all the Spring advisors found according to superclass rules.
        // 找到xml方式配置的Advisor和原生介面的AOP的advisor 以及找到事務相關的advisor
        List<Advisor> advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        // 將找到的aspect, 封裝為一個Advisor
        if (this.aspectJAdvisorsBuilder != null) {
            //buildAspectJAdvisors()方法就是用來解析切面類, 判斷是否含有@Aspect註解, 然後將每一個通知生成一個advisor
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        // 返回所有的通知
        return advisors;
    }

 

這裡做了兩件事

第一步: 解析xml方式配置的Advisor (包括原生介面方式配置的advisor 以及找到事務相關的advisor)

第二步: 解析註解方式的切面. buildAspectJAdvisors()方法是用來解析切面類的. 解析每一個切面類中的通知方法, 併為每個方法匹配切點表示式.

 5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

public List<Advisor> buildAspectJAdvisors() {
    /*
     * aspectNames: 用於儲存切面名稱的集合
     * aspectNames是快取的類級別的切面, 快取的是已經解析出來的切面資訊
     */
    List<String> aspectNames = this.aspectBeanNames;

    // 如果aspectNames值為空, 那麼就在第一個單例bean執行的時候呼叫後置處理器(AnnotationAwareAspectJAutoProxy)
    if (aspectNames == null) {
      // 加鎖, 防止多個執行緒, 同時載入 Aspect
      synchronized (this) {
        aspectNames = this.aspectBeanNames;
        // 雙重檢查
        if (aspectNames == null) {
          // 儲存所有從切面中解析出來的通知
          List<Advisor> advisors = new ArrayList<>();
          // 儲存切面名稱的集合
          aspectNames = new ArrayList<>();
          /*
           * 掃描Object的子類. 那就是掃描所有的類
           *
           * 這裡傳入要掃描的物件是Object.class. 也就是說去容器中掃描所有的類.
           * 迴圈遍歷. 這個過程是非常耗效能的, 所以spring增加了快取來儲存切面
           *
           * 但事務功能除外, 事務模組是直接去容器中找到Advisor型別的類 選擇範圍小
           * spring 沒有給事務模組加快取
           */
          String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
              this.beanFactory, Object.class, true, false);

          // 迴圈遍歷beanNames
          for (String beanName : beanNames) {
            if (!isEligibleBean(beanName)) {
              continue;
            }
            // We must be careful not to instantiate beans eagerly as in this case they
            // would be cached by the Spring container but would not have been weaved.
            // 通過beanName去容器中獲取到對應class物件
            Class<?> beanType = this.beanFactory.getType(beanName);
            if (beanType == null) {
              continue;
            }
            // 判斷bean是否是一個切面, 也就是腦袋上是否有@Aspect註解
            if (this.advisorFactory.isAspect(beanType)) {
              aspectNames.add(beanName);
              // 將beanName和class物件構建成一個AspectMetadata物件
              AspectMetadata amd = new AspectMetadata(beanType, beanName);
              if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                MetadataAwareAspectInstanceFactory factory =
                    new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                // 解析切面類中所有的通知--一個通知生成一個Advisor.
                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

                // 加入到快取中
                if (this.beanFactory.isSingleton(beanName)) {
                  this.advisorsCache.put(beanName, classAdvisors);
                } else {
                  this.aspectFactoryCache.put(beanName, factory);
                }
                advisors.addAll(classAdvisors);
              } else {
                // Per target or per this.
                if (this.beanFactory.isSingleton(beanName)) {
                  throw new IllegalArgumentException("Bean with name '" + beanName +
                      "' is a singleton, but aspect instantiation model is not singleton");
                }
                MetadataAwareAspectInstanceFactory factory =
                    new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                this.aspectFactoryCache.put(beanName, factory);
                advisors.addAll(this.advisorFactory.getAdvisors(factory));
              }
            }
          }
          this.aspectBeanNames = aspectNames;
          return advisors;
        }
      }
    }

    if (aspectNames.isEmpty()) {
      return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
        advisors.addAll(cachedAdvisors);
      } else {
        MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
        advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
    }
    return advisors;
  }

 

我們來看看如何生成List<Advisor>的

// 解析切面類中所有的通知--一個通知生成一個Advisor.
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        // 獲取標記了@Aspect的類
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        // 獲取切面類的名稱
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        // 驗證切面類
        validate(aspectClass);

        // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
        // so that it will only instantiate once.
        // 使用包裝的模式來包裝 aspectInstanceFactory, 構建成MetadataAwareAspectInstanceFactory類
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

        // 通知的集合, 按照排序後
        List<Advisor> advisors = new ArrayList<>();
        // 獲取切面類中所有的通知方法, 除了帶有@Pointcut註解的方法
        for (Method method : getAdvisorMethods(aspectClass)) {
            // 將候選方法解析為Advisor. Advisor中包含advise和pointcut. 注意: getAdvisor()方法中定義了切面解析的順序
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        // If it's a per target aspect, emit the dummy instantiating aspect.
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        // Find introduction fields.
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

 

這裡主要有兩點, 第一個是getAdvisorMethods(aspectClass)獲取當前切面類的所有的AdvisorMethod , 第二個是封裝成的Advisor物件

  • 第一步: 解析切面類中所有的通知方法.getAdvisorMethods(aspectClass)
    /**
         * 獲取切面類中所有的方法, 且方法中有@Pointcut註解
         * @param aspectClass
         * @return
         */
        private List<Method> getAdvisorMethods(Class<?> aspectClass) {
            final List<Method> methods = new ArrayList<>();
            // 呼叫doWithMethods. 第二個引數是一個匿名函式, 重寫了doWith方法
            ReflectionUtils.doWithMethods(aspectClass, method -> {
                // 解析切面類中所有的方法, 除了Pointcut
                if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
                    methods.add(method);
                }
            }, ReflectionUtils.USER_DECLARED_METHODS);
            if (methods.size() > 1) {
                // 對方法進行排序
                methods.sort(METHOD_COMPARATOR);
            }
            return methods;
        }

     

這個方法是, 掃描切面類的所有方法, 將其新增到methods中, 除了Pointcut註解的方法

然後對methods進行排序, 如何排序呢?

private static final Comparator<Method> METHOD_COMPARATOR;

    static {
        Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
                new InstanceComparator<>(
                        Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
                (Converter<Method, Annotation>) method -> {
                    AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                    return (ann != null ? ann.getAnnotation() : null);
                });
        Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
        METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
    }

 

按照Aroud, Before, After, AferReturning, AfterThrowing的順序對通知方法進行排序

  • 第二步: 將候選的方法解析為Advisor. 這裡也是有兩步.具體如下:
    /**
         * 解析切面類中的方法
         * @param candidateAdviceMethod 候選的方法
         */
        @Override
        @Nullable
        public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
                int declarationOrderInAspect, String aspectName) {
    
            validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    
            // 獲取切面中候選方法的切點表示式
            AspectJExpressionPointcut expressionPointcut = getPointcut(
                    candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
            if (expressionPointcut == null) {
                return null;
            }
    
            // 將切點表示式和通知封裝到InstantiationModelAwarePointcutAdvisorImpl物件中, 這是一個Advisor通知
            return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                    this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
        }

     

在getPointcut中解析了method,以及切點表示式pointcut

/**
     * 找到候選方法method屬於哪一種型別的Aspectj通知
     * @param candidateAdviceMethod        候選的通知方法
     * @param candidateAspectClass        候選的切面類
     * @return
     */
    @Nullable
    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
        // 第一步: 解析候選方法上的註解,類似@Before(value="pointcut()")
        // 找到Aspectj註解:  @Pointcut, @Around, @Before, @After, @AfterReturning, @AfterThrowing
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }

        // 第二步: 解析aspect切面中的切點表示式
        AspectJExpressionPointcut ajexp =
                new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
        // 解析切點表示式
        ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
        if (this.beanFactory != null) {
            ajexp.setBeanFactory(this.beanFactory);
        }
        return ajexp;
    }

 

如上程式碼, 可知, 這裡也是有兩個操作. 分別是將method解析為Advise, 另一個是解析切面類中的pointcut切點表示式. 返回返回切點表示式.

接下來, 就是將候選方法和切點表示式封裝成Advisor. 在getAdvisor(...)方法中:

// 將切點表示式和通知封裝到InstantiationModelAwarePointcutAdvisorImpl物件中, 這是一個Advisor通知
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
      this, aspectInstanceFactory, declarationOrderInAspect, aspectName);

 

expressionPointcut: 即切點表示式; candidateAdviceMethod: 即候選方法

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
            Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

        // 當前的切點
        this.declaredPointcut = declaredPointcut;
        // 切面類
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
        // 切面方法名
        this.methodName = aspectJAdviceMethod.getName();
        //切面方法引數的型別
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
        //切面方法物件
        this.aspectJAdviceMethod = aspectJAdviceMethod;
        // aspectJ的通知工廠
        this.aspectJAdvisorFactory = aspectJAdvisorFactory;
        // aspectJ的例項工廠
        this.aspectInstanceFactory = aspectInstanceFactory;
        // advisor的順序
        /**
         * 前面我們看到, Advisor會進行排序, Around, Before, After, AfterReturning, AfterThrowing, 按照這個順序.
         * 那麼order值是什麼呢?是advisors的size. 如果size是0, 那麼就是第一個方法. 這裡第一個不一定是Around, 他可能沒有Around通知, 也沒有Before通知.
         */
        this.declarationOrder = declarationOrder;
        // 切面名
        this.aspectName = aspectName;

        if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            // Static part of the pointcut is a lazy type.
            Pointcut preInstantiationPointcut = Pointcuts.union(
                    aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

            
            this.pointcut = new PerTargetInstantiationModelPointcut(
                    this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
            this.lazy = true;
        }
        else {
            // A singleton aspect.
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
            this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
        }
    }

 

前面已經得到了切入點表示式, 這裡會進行初始化Advice, 初始化的時候, 根據通知的型別進行初始化.

  • 環繞通知, 構建一個環繞通知的物件
  • 前置通知, 構建一個前置通知的物件
  • 後置通知, 構建一個後置通知的物件
  • 異常通知, 構建一個異常通知的物件
  • 返回通知, 構建一個返回通知的物件

具體程式碼如下: 

@Override
    @Nullable
    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        // 候選的切面類
        Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        validate(candidateAspectClass);
        // 通知方法上的註解內容
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }

        // If we get here, we know we have an AspectJ method.
        // Check that it's an AspectJ-annotated class
        if (!isAspect(candidateAspectClass)) {
            throw new AopConfigException("Advice must be declared inside an aspect type: " +
                    "Offending method '" + candidateAdviceMethod + "' in class [" +
                    candidateAspectClass.getName() + "]");
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Found AspectJ method: " + candidateAdviceMethod);
        }

        AbstractAspectJAdvice springAdvice;

        switch (aspectJAnnotation.getAnnotationType()) {
            case AtPointcut:
                if (logger.isDebugEnabled()) {
                    logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }
                return null;
            case AtAround:
                // 封裝成環繞通知的物件
                springAdvice = new AspectJAroundAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtBefore:
                // 封裝成前置通知物件
                springAdvice = new AspectJMethodBeforeAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtAfter:
                // 封裝成後置通知物件
                springAdvice = new AspectJAfterAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtAfterReturning:
                // 封裝成返回通知物件
                springAdvice = new AspectJAfterReturningAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    springAdvice.setReturningName(afterReturningAnnotation.returning());
                }
                break;
            case AtAfterThrowing:
                // 封裝異常通知物件
                springAdvice = new AspectJAfterThrowingAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            default:
                throw new UnsupportedOperationException(
                        "Unsupported advice type on method: " + candidateAdviceMethod);
        }

        // Now to configure the advice...
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrder);
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            springAdvice.setArgumentNamesFromStringArray(argNames);
        }
        springAdvice.calculateArgumentBindings();

        return springAdvice;
    }

 

這就是我們在之前的結構中說過的, 在解析切面的時候, 會解析切面中的每一個方法, 將其解析成一個Advisor, 而每一個Advisor都包含兩個部分:Advise和pointcut.

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

最後, 將所有的切面類都解析完, 將所有的Advisor放入到集合advisors中返回.

 

這樣就完成了切面的解析.

 

2) 呼叫動態代理

在ioc解析的過程中, 是在什麼時候建立動態代理的呢?

通常是在建立bean初始化之後建立動態代理. 如果有迴圈依賴, 會在例項化之後建立動態代理, 再來感受一下建立bean過程中的操作.

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 

下面我們來看正常的流程, 在初始化之後建立AOP動態代理 .

在建立bean的過程中,一共有三步, 來看看AbstractAutowireCpableBeanFactory.doCreateBean()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //第一步: 例項化
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        // 這裡使用了裝飾器的設計模式
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        ......
        try {
            // 第二步:填充屬性, 給屬性賦值(呼叫set方法)  這裡也是呼叫的後置處理器
            populateBean(beanName, mbd, instanceWrapper);
            // 第三步: 初始化.
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        ......
    }

 

在第三步初始化的時候, 要處理很多bean的後置處理器. 

@Override
    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;
    } 

 

postProcessAfterInitialization(result, beanName);就是處理初始化之後的後置處理器, 下面就從這個方法作為入口分析. 

AnnotationAwareAspectJAutoProxyCreator也實現了postProcessAfterInitialization(result, beanName);介面

@Override
    public Object  postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        /**
         * 每一個bean在解析的時候都會解析一遍切面.
         * 為什麼每次都要解析一遍呢? 因為還有另外一種方式-實現Advisor介面的方式實現AOP, 在載入過程中, 可能隨時有新的bean被解析出來. 所以, 需要每次都重新解析一遍,.
         * 我們在第一次解析的Advisor都已經放入到快取, 在這裡會先從快取中取, 也就是已經解析過的不會重複解析. 也就是不 消耗效能
         */
        if (bean != null) {
            // 獲取快取key
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            /**
             * 因為有可能在迴圈依賴處理的時候已經建立國一遍, 如果是那麼現在就不再建立了,並且刪除
             * 在這裡, 我們要處理的是普通類的動態代理, 所以, 需要將迴圈以來建立的動態代理刪掉
             */
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                // 該方法將返回動態代理的例項
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

 

這裡需要強調的一點是, 每一個bean在解析的時候都會解析一遍切面.為什麼每次都要解析一遍呢?
因為建立切面有兩種方式, 一種是實現Advisor介面, 另一種是註解的方式. 實現Advisor介面的方式, 在載入過程中, 可能隨時有新的bean被解析出來. 所以, 需要每次都重新解析一遍.
我們在第一次解析的Advisor都已經放入到快取, 在這裡會先從快取中取, 也就是已經解析過的不會重複解析. 也就是不 消耗效能

 

接下來處理的流程如下:

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 

這裡,第三步:刪除迴圈依賴建立的動態代理物件, 為什麼要這樣處理呢?

因為有可能在迴圈依賴處理的時候已經建立了動態代理bean, 如果是,那麼現在就不再建立了,並且將其刪除. 
在這裡, 我們要處理的是普通類的動態代理, 所以, 需要將迴圈依賴建立的動態代理刪掉
 
注: earlyProxyReferences物件使用來儲存迴圈依賴過程中建立的動態代理bean. 如果迴圈依賴建立了這個代理bean, 那麼直接返回, 如果沒有建立過, 我們再建立.
 
下面來看看是如何建立的?
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 已經被處理過(解析切面的時候, targetSourcedBeans用來儲存自己實現建立動態代理的邏輯)
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        // 判斷bean是否是需要增強的bean
        /**
         * 哪些類是不需要增強的呢?
         * 在解析切面的時候, 基礎類和應該跳過的類是不需要增強的.
         */
        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;
        }

        // 匹配Advisor. 根據類匹配advisors, 至少匹配上一個, 才建立動態代理, 否則不建立動態代理
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

        // 匹配了至少一個advisor, 建立動態代理
        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;
    }

 

來看看建立流程
5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 

 首先判斷是否是需要跳過的類. 哪些類是需要跳過的類呢?

第一類:基礎類. Advice, Pointcut, Advisor, AopInfrastructureBean. 

第二類: 原始的介面類, 以.ORIGINAL開頭的類

 

 接下來, 匹配Advisor. 

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        // 第一步: 拿到已經解析出來的advisors(這次是從快取中獲取)
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        // 第二步:迴圈判斷advisor能否作用於當前bean(原理: 切點是否命中bean)
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
       // 第三步: 對匹配bean的advisor進行增強
        extendAdvisors(eligibleAdvisors);
        // 第四步: 對匹配bean的advisor進行排序
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        // 返回匹配到的advisors
        return eligibleAdvisors;
    } 

 

這裡經過了四步, 具體詳見上述程式碼及註釋. 

  • 第一步: 從快取中拿到已經解析出來的advisors
  • 第二步:迴圈判斷advisor能否作用於當前bean
  • 第三步: 對匹配bean的advisor進行增強
  • 第四步: 對匹配bean的advisor進行排序

 5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 這裡面的第一步: 從快取中取出了已經解析出來的advisors集合. 解析方式是從快取中取出已經解析的advisors

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 接下來,迴圈遍歷獲得到的advisors, 得到每一個advisor. 判斷advisor是否是目標bean需要增強的通知.

 5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 這裡在篩選的時候, 根據切點表示式進行了兩次篩選. 第一次粗篩, 第二次是精篩. 整個目標類, 只要有一個類命中切點表示式, 那麼這個類就是需要被建立動態代理的類, 返回true.

接下來就是要建立動態代理了. 然後,返回建立的動態代理物件.

下面來看看是如何建立動態代理的.

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析  

建立動態代理物件有兩種方式: 一種是jdk代理, 一種是cglib代理. 

無論是使用xml配置的方式, 還是使用註解的方式, 都有一個引數proxy-target-class, 如果將這個引數設定為true, 表示強制使用cglib代理. 如下所示設定:

使用註解的方式
@EnableAspectJAutoProxy(proxyTargetClass=true)

使用xml配置的方式
<aop: sapectj-autoproxy proxy-target-class="true"></aop:>

所以在建立動態代理之前, 先解析註解或者配置, 看是否配置了proxy-target-class引數. 如果配置了這個引數,且其值為true, 那麼就建立一個cglib代理物件. 否則建立一個JDK代理物件.通常, 我們使用的更多的是spring自己定義的JDK代理物件. 通過Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);建立動態代理


在JDKDynamicAopProxy代理類中有一個invoke()方法. 這個invoke方法, 就是執行代理物件的方法時呼叫的方法.

 該方法是通過反射的方法執行目標類中定義的方法的. 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 

}

 3. 呼叫動態代理.

呼叫這裡有一個非常經典的呼叫邏輯--呼叫鏈.

 5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 如上圖, 呼叫鏈的邏輯是, 呼叫動態代理方法,比如說div(arg1, arg2), 然後執行呼叫鏈中第一個通知advisor1, 然後第一個通知呼叫第二個通知, 在執行第二個, 以此類推, 當所有的通知執行完, 呼叫目標方法div(arg1, arg2), 然後返回執行結果. 我們來看看程式碼的邏輯實現.

如下程式碼是呼叫動態代理的程式碼入口:

 

public class LxlMainClass {
  public static void main(String[] args) {

    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);

    Calculate calculate = (Calculate) ctx.getBean("lxlCalculate");
    /**
     * 上面的calculate, 就是返回的動態代理的類
     * 當呼叫下面的div方法時, 實際上呼叫的是JdkDynamicAopProxy.invoke(...)方法
     */
    calculate.div(2, 4);

    ProgramCalculate programCalculate = (ProgramCalculate) ctx.getBean("lxlCalculate");
    String s = programCalculate.toBinary(5);
    System.out.println(s);
  }
}

我們在main方法中, 獲取的Calculate物件, 其實是動態代理生成的物件. 當呼叫calculate.div(2, 4)方法時, 其實呼叫的是動態代理的invoke()方法.

@Override
    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        // 設定代理上下文
        boolean setProxyContext = false;

        // 目標源: 也就是目標代理的目標類
        TargetSource targetSource = this.advised.targetSource;
        Object target = null;

        try {

            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            // 如果方法所在類是一個介面 && 是可分配為Advised型別的方法
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // 把代理物件暴露線上程變數中.
                oldProxy = AopContext.setCurrentProxy(proxy);
                // 設定代理的上下文為true
                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);

            // 把aop的advisor全部轉化為攔截器, 通過責任鏈模式依次呼叫
            /**
             * 將advisor物件轉換為interceptor物件.
             *
             * 問題: 為什麼要將advisor都轉化為interceptor攔截器呢?
             * 主要還是因為要進行責任鏈呼叫. 之前說過, 要想進行責任鏈呼叫, 他們要有一個共同的方法.
             * 轉化為interceptor以後, 這裡共同的方法就是invoke().
             * beforeAdivsor, afterAdvisor, returningAdvisor, throwingAdvisor. 這幾種型別. 只有returningAdvisor和throwingAdvisor會轉化為Interceptor.
             * 因為beforeAdvisor和adgerAdvisor本身就實現了interceptor介面
             */
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // 攔截器鏈為空
            if (chain.isEmpty()) {
                // 通過反射直接呼叫執行目標方法
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // 建立一個 method invocation 攔截器
                MethodInvocation invocation =
                        new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                // 通過攔截器鏈呼叫連線點
                retVal = invocation.proceed();
            }

            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

 

 這裡有兩步很重要:

第一步: 將匹配的advisor轉換為Interceptor

第二步: 呼叫責任鏈, 執行各類通知

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 

 

 

先看第一步: 將匹配的advisor物件轉換為interceptor攔截器物件. 為什麼要將advisor轉換為interceptor攔截器呢?

因為要進行責任鏈呼叫. 前面說過, 要想進行責任鏈呼叫, 他們要有一個共同的方法. 轉化為interceptor以後, 共同的方法就是invoke().

@Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, @Nullable Class<?> targetClass) {

        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        // 獲取到匹配當前方法的所有advisor
        Advisor[] advisors = config.getAdvisors();
        List<Object> interceptorList = new ArrayList<>(advisors.length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        Boolean hasIntroductions = null;

        for (Advisor advisor : advisors) {
            /**
             * 如果advisor是PointcutAdvisor型別
             */
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                // 註解配置資訊是一個前置過濾器 或者 目標類匹配advisor的切點表示式
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    boolean match;
                    if (mm instanceof IntroductionAwareMethodMatcher) {
                        if (hasIntroductions == null) {
                            hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                        }
                        match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                    }
                    else {
                        match = mm.matches(method, actualClass);
                    }
                    if (match) {
                        // 將advice轉換為MethodInterceptor攔截器,
                        MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                // 將MethodInterceptor攔截器和MethodMatcher組裝為一個新的物件
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            // 將攔截器直接放到interceptorList中
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            else if (advisor instanceof IntroductionAdvisor) { // 如果advisor是IntroductionAdvisor型別
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            else { // 其他型別的advisor
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }

        return interceptorList;
    }

 

這裡最重要的方法就是registry.getInterceptors(advisor), 在getInterceptors(advisor)裡面迴圈遍歷了advisors, 然後將每一個advisor轉換為Interceptor, 這是將advisor轉換為interceptor的具體實現. 

我們來看看原始碼和邏輯

@Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            // 如果advice已經實現了MethodInterceptor介面, 那麼直接將其新增到interceptors集合中
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // 判斷是否是指定型別的advice
            if (adapter.supportsAdvice(advice)) {
                // 如果是就將其轉換為對應型別的Interceptor
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[0]);
    }

 

adapter.supportsAdvice(advice)判斷advice是否是指定型別的adapter. adapter有如下幾種

  • MethodBeforeAdviceAdapter : 前置通知adapter
  • AfterReturningAdviceAdapter:後置|放回通知adapter
  • SimpleBeforeAdviceAdapter: simpler前置通知adapter
  • ThrowsAdviceAdapter:異常通知adapter

這裡採用的是介面卡模式, 通過介面卡來匹配各種不同型別的通知. 然後再呼叫adapter.getInterceptor(advisor)將advisor構建成Interceptor.

通常有beforeAdivsor, afterAdvisor, returningAdvisor, throwingAdvisor幾種型別的通知. 只有returningAdvisor和throwingAdvisor會轉化為Interceptor.
因為beforeAdvisor和afterAdvisor本身就實現了interceptor介面.

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

將所有的advisor轉換成Interceptor以後放入到interceptors集合中返回.

 

接下來執行責任鏈呼叫.責任鏈呼叫的思想主要有兩個

1. 遞迴呼叫

2. 所有的advisor最終都讓其實現interceptor, 並重寫invoke()方法.

來看一下原始碼

@Override
    @Nullable
    public Object proceed() throws Throwable {
        // We start with an index of -1 and increment early.
        // 如果是最後一個攔截器, 則直接執行. invokeJoinpoint()方法
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        // 取出interceptorsAndDynamicMethodMatchers物件
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        // 如果是InterceptorAndDynamicMethodMatcher型別
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
            // 呼叫methodMather的matchs()方法
            if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
                // 匹配成功, 則呼叫攔截器的invoke()方法
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                // 動態匹配失敗, 跳過此攔截器, 呼叫攔截器鏈中的下一個攔截器
                return proceed();
            }
        }
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            //它是一個攔截器,因此我們只需要呼叫它:切入點將在構造此物件之前進行靜態評估。
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

 

在這裡interceptorsAndDynamicMethodMatchers存放的就是所有匹配到的advisor. 按照順序,取出advisor. 然後將其轉換為MethodInterceptor以後, 呼叫他的invoke(this)方法,同時將傳遞當前物件, 在invoke(this)中在此呼叫proceed()方法. 迴圈呼叫. 從interceptorsAndDynamicMethodMatchers取advisor, 直到取出最後一個advisor. 再次呼叫proceed()則指定呼叫目標方法.

interceptorsAndDynamicMethodMatchers裡面一共有6個advisor

具體呼叫如下圖:

5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析

 以上就是呼叫aop的整個過程. 內容還是很多的,需要時間消化.

相關文章