Java核心知識體系4:AOP原理和切面應用

1 概述

我們所說的Aop(即面向切面編程),即面向介面,也面向方法,在基於IOC的基礎上實現。
Aop最大的特點是對指定的方法進行攔截並增強,這種增強的方式不需要業務程式碼進行調整,無需侵入到業務程式碼中,使業務與非業務處理邏輯分離。
以Spring舉例,通過事務的註解配置,Spring會自動在業務方法中開啟、提交業務,並且在業務處理失敗時,執行相應的回滾策略。
aop的實現主要包括了兩個部分:

  • 匹配符合條件的方法(Pointcut)
  • 對匹配的方法增強(JDK代理、cglib代理)
    spring針對xml配置和配置自動代理的Advisor有很大的處理差別,在IOC中主要是基於XML配置分析的,在AOP的源碼解讀中,則主要從自動代理的方式解析,分析完註解的方式,再分析基於xml的方式。

2 案例分析

下面是spring aop的用法 也是用於源碼分析的案例
切面類:TracesRecordAdvisor

@Aspect
@Component
public class TracesRecordAdvisor {
    
    @Pointcut("execution(* spring.action.expend.aop.services.*.*(..))")
    public void expression() {
    }

    @Before("expression()")
    public void beforePrint()
    {
        System.out.println("進入服務,在服務執行之前,記錄日誌....");
    }

    @AfterReturning("expression()")
    public void afterPrint()
    {
        System.out.println("退出服務,在服務執行結束之後,記錄日誌.....");
    }
}

xml配置: aop的註解啟用只需要在xml中配置這段程式碼即可,這個是作為入口

<aop:aspectj-autoproxy/>

服務類:PayServiceImpl 使用jdk代理 所以要有一個介面

@Service
public class PayServiceImpl implements PayService {
    public void payMoneyMenthod() {
        System.out.println("正在執行付款...");
    }
}

測試方法:

    @Test
    public void springAopTestService() {
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-aop.xml");
        PayService payService= (PayService) applicationContext.getBean("payServiceImpl");
        payService.payMoneyMenthod();
    }

執行結果:

進入服務,在服務執行之前,記錄日誌....
正在執行付款...
退出服務,在服務執行結束之後,記錄日誌.....

從上面的執行結果看,payMoneyMenthod 方法的確是被增強了。

3 BeanFactoryPostProcessor

讀spring源碼的時候,可以首先看下BeanFactoryPostProcessor和BeanPostProcess,這兩個介面都是在spring通過配置文件或者xml獲取bean聲明,生成BeanDefinition後,允許我們再對生成的BeanDefinition,進行入口包裝和增強。
我們看看BeanFactoryPostProcessor的定義

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}

方法postProcessBeanFactory的參數為ConfigurableListableBeanFactory,我們之前討論過beanFactory用來獲取bean的,而ConfigurableListableBeanFactory繼承介面SingletonBeanRegistry和BeanFactroy,所以可以訪問到已經生成過的BeanDefinitions集合,如果某個類實現該介面,spring會註冊這個類,然後執行這個類的postProcessBeanFactory方法,以便我們對BeanDefinition進行擴展。
接下來的程式碼表示Spring是如何註冊BeanFactoryPostProcessor並執行postProcessBeanFactory的。

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            prepareRefresh();
            //核心方法1
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            prepareBeanFactory(beanFactory);
            try {
                postProcessBeanFactory(beanFactory);
                //核心方法2 執行BeanFactoryPostProcessor
                invokeBeanFactoryPostProcessors(beanFactory);
                //核心方法 3 註冊BeanPostProcessor
                registerBeanPostProcessors(beanFactory);
                // Initialize message source for this context.
                initMessageSource();
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
                // Initialize other special beans in specific context subclasses.
                onRefresh();
                // Check for listener beans and register them.
                registerListeners();
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
                // Last step: publish corresponding event.
                finishRefresh();
            }
            catch (BeansException ex) {
                 ............
                throw ex;
            }
            finally {
                 ............
                resetCommonCaches();
            }
        }
    }

核心方法1obtainFreshBeanFactory就是前兩篇所說的生成BeanDefinition的入口,invokeBeanFactoryPostProcessors核心方法2就是執行BeanFactoryPostProcessor介面的方法。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}

通過方法getBeanFactoryPostProcessors獲取註冊BeanFactoryPostProcessor,然後來看看如何添加一個處理器

@Override
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
   this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
}

對於方法invokeBeanFactoryPostProcessors不再往下看了,裡面的方法大致先對BeanFactoryPostProcessor進行排序,排序的標準是是否實現了PriorityOrdered,然後根據設置的order大小指定執行順序,生成一個排序集合和一個普通的集合,最後執行invokeBeanFactoryPostProcessors

private static void invokeBeanFactoryPostProcessors(
  Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
  for (BeanFactoryPostProcessor postProcessor : postProcessors) {
    //執行到自定義的BeanFactoryPostProcessor
 postProcessor.postProcessBeanFactory(beanFactory);
   }
}

這個方法就會循環先前註冊的BeanFactoryPostProcessor集合,然後執行postProcessBeanFactory。

4 BeanPostProcess 解讀

與BeanFactoryPostProcessor相比,BeanPostProcess就重要得多了,因為Spring的註解、AOP等都是通過這個介面的方法攔截執行的,它貫穿了Bean創建過程的整個生命周期,在IOC階段,Spring只註冊BeanPostProcess,執行則放到了Bean的實例化創建階段。
首先看下BeanPostProcessor的介面定義

public interface BeanPostProcessor {
        //在bean創建 屬性賦值之後  Aware介面執行之後執行
        Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
        //在init-method afterPropertiesSet 執行之後執行
        Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    }

在bean的聲明周期中,下面的序列是bean創建後要執行的介面和方法順序:

  • 實例化(autowireConstructor或者instantiateBean)
  • 屬性初始化(populateBean)
  • Aware介面(如果你的 bean 有進行實現)
  • BeanPostProcess.postProcessBeforeInitialization
  • PostConstructInitializingBean.afterPropertiesSet
  • BeanPostProcess.postProcessAfterInitialization

其中通過註解引入依賴的方式就是在AutowiredAnnotationBeanPostProcessor這個類中實現的,而接下來要分析的Spring Aop也是從這裡開始的,這個類叫AnnotationAwareAspectJAutoProxyCreator,

5 NameSpaceHanlder 解讀

在Spring中,任何的技術都是在IOC的基礎上進行的,Aop也不例外,程式會首先讀取xml配置文件,然後對讀取到的標籤先查找命名空間,然後找對應的NameSpaceHandler,最終調用parse方法解析標籤。
aop標籤的解析,使用純註解的方式aop:aspectj-autoproxy和使用aop:config的配置解析不太一樣,具體表現在生成PointCut和生成Before、After、Around等切面類時,使用aop:config的方式會為這些註解生成一個BeanDefinition,而這個BeanDefinition的構造函數是由3個BeanDefinition組成,表明這個類是合成類,即synthetic這個屬性為true。然後跟解析普通的bean一樣,生成這些實例對象,後面的過程就跟是用純註解的方式相同了,接下來的分析是基於純註解分析的,也就是解析從解析aop:aspectj-autoproxy這個標籤開始。
前面的xml文件的標籤解析是通過parseDefaultElement方法解析默認的標籤的,而我們在配置文件裡面配置了啟動自動代理的方式<aop:aspectj-autoproxy/>,當Spring讀取到這個標籤,則會走parseCustomElement(root)這個方法了,這個方法的源碼不再解析,主要完成的功能如下:

  • 獲取element的nameSpaceUri,根據nameSpaceUri找到NameSpaceHanlder
  • 調用NameSpaceHanlder的parse方法解析element

下面是NameSpaceHanlder介面的定義

public interface NamespaceHandler {
        void init();
        BeanDefinition parse(Element element, ParserContext parserContext);
        BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext       parserContext);
    }

這裡面的init方法是我們初始化操作的,這裡可以完成對指定的標籤設置解析器,然後再parse方法裡面找到指定標籤的解析器,然後調用該解析器的parse方法解析標籤,後面會重點看這兩個方法。
再來看下Spring如何載入NameSpaceHanlder的,Spring首先會取查找項目空間下目錄META-INF/的所有spring.handlers文件,這個文件是在Spring依賴的jar下面,在核心jar包都會由這個文件,aop的jar包路徑下文件內容為:spring.handlers

//www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

發現這裡面存儲的是一個key,value,key是aop的nameSpaceUri,value是AopNamespaceHandler,從這個類名上就能發現該類實現了NamespaceHandler,肯定也就實現了init和parse方法,所以解析<aop:aspectj-autoproxy/>的任務就由AopNamespaceHandler的parse完成。
查看AopNamespaceHandler的init方法

    @Override
    public void init() {
        // In 2.0 XSD as well as in 2.1 XSD.
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new                       AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
        // Only in 2.0 XSD: moved to context namespace as of 2.1
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }

上面的程式碼就很清晰了,<aop:config> 標籤由ConfigBeanDefinitionParser處理,<aop:aspectj-autoproxy/> 則由AspectJAutoProxyBeanDefinitionParser這個類處理,這兩種處理其實對應了自動代理和通過xml配置的處理方式,然後會調用AspectJAutoProxyBeanDefinitionParser的parse方法

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
    }

這個方法其實就是為了註冊一個AnnotationAwareAspectJAutoProxyCreator類,然後AOP的所有處理邏輯都會交給這個類處理,由於這個類的實現了BeanPostProcessor,所以這個類的入口就是BeanPostProcessor介面的兩個方法:

  • postProcessBeforeInitialization
  • postProcessAfterInitialization

6 Spring Aop 源碼分析

我們前面分析了,當spring讀取xml文件遇到<aop:aspectj-autoproxy/>會找到AopNamespaceHandler這個處理類,然後這個類又將這個標籤委託給了AspectJAutoProxyBeanDefinitionParser類,最終調用這個類得parse方法,parse方法未做分析,其實這個方法的目的很簡單,就是註冊AnnotationAwareAspectJAutoProxyCreator這個類,這個類實現了BeanPostProcessor和InstantiationAwareBeanPostProcessor介面,最終在實例化bean對象也就是執行BeanFactory.getBean(beanName)的過程中,會調用這兩個介面的方法(執行順序如下):
InstantiationAwareBeanPostProcessor先執行:

postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
postProcessAfterInstantiation(Object bean, String beanName)

BeanPostProcessor再執行:

postProcessBeforeInitialization(Object bean, String beanName)
Object postProcessAfterInitialization(Object bean, String beanName)

AOP的實現基本上是在這兩個方法中進行的,所以就從這裡來看Spring是如何實現AOP的,Spring的AOP代理目前支援方法的增強,看源碼目前好像也支援了屬性的增強了。
讀取源碼前首先來分析一下方法增強的原理,有助於我們讀取源碼時緊緊抓住主線。首先第一個問題,如果我們想對一個類的方法進行增強,我們應該怎麼做呢?
這種業務需求可以通過代理實現,在方法執行前,攔截這個方法,並且加入要執行增強的邏輯,最後再執行目標方法。下面是Spring用的兩種代理方式:

JDK代理:我們可以通Proxy類獲取一個目標類的代理對象,但JDK代理要求被代理的類必須實現介面,所以是基於介面的代理。

cglib代理:如果目標類沒有介面,使用cglib代理,是由asm封裝的,直接操作類得位元組碼,效率也很高。

由於在生產業務中,我們不可能對所有的類都執行增強,所以還需要一個選擇器,將符合條件的bean進行增強,Spring使用了PointCut介面,通過該介面的getMethodMatcher方法獲取一個方法匹配器,然後通過matches方法匹配到目標類對象的目標方法執行增強操作。mathcer匹配規則就是通過Spring 配置的expression表達式了。

所以在分析源碼的時,要圍繞這兩方面進行:

  • 匹配切點方法(構建切入點表達式類和切面類)
  • 創建代理對象

這兩方面在Spring的實現里非常複雜,尤其是第一步匹配切點方法過程,這個過程中,Spring會將@Aspect註解類的@Before,@After,@Around、@Pointcut等註解都封裝成待執行的切面方法類,然後通過方法匹配器匹配到的要增強的方法前後執行切面方法類,達到方法增強的目的。
第二階段,創建代理對象默認是通過JDK代理實現配置,<aop:aspectj-autoproxy proxy-target-class=”true”>這樣配置可以指定使用cglib代理。

7 註解切面代理類分析

上面分析了真正實現AOP功能的是AnnotationAwareAspectJAutoProxyCreator,由於這個類實現了BeanPostProcessor和InstantiationAwareBeanPostProcessor,所以在創建一個bean的時候,會進入到這兩個介面的方法,這兩個介面包含了四個方法,方法執行順序上面已經分析過了,來看看這個類的類圖:
image
類圖上比較重要的介面就是右上角實現的兩個介面,在bean創建的生命周期過程中,會校驗當前容器中是否註冊了實現了這兩個介面的類,如果有則調用介面的方法,前面的分析中在解析aop:aspectj-autoproxy/時,將這個類註冊到了容器中,而且上面也羅列了這兩個介面中四個方法的調用順序,在這個類中完成主要功能的2個方法及其執行順序:

  • InstantiationAwareBeanPostProcessor先執行:
postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
  • BeanPostProcessor再執行:
Object postProcessAfterInitialization(Object bean, String beanName)

postProcessBeforeInstantiation方法主要是找出註解了Advice的類,並將Advice的類使用了@Before,@After,@Around、@Pointcut,@AfterThrowing等註解的方法封裝成一個一個類放入到快取中供匹配到的類生成代理用。postProcessAfterInitialization主要是匹配符合條件的目標類對象,然後生成代理的過程,接下來就按順序分析這兩個方法完成的功能。

8 Aspect註解類分析

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        //構建一個快取key
        Object cacheKey = getCacheKey(beanClass, beanName);
        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
            //如果當前beanClass的快取key 存在於Class為Advise的快取中,表示當前的beanClass是Adivse類
            //而且不需要生成代理。
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            //核心校驗:1 當前類是否是AOP的基礎類 2、當前類是否應該跳過不生成代理
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
        //這部分主要是用於實現了TargetSource介面的bean,然後從getTarget中獲取對象 創建代理
        if (beanName != null) {
            TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
            if (targetSource != null) {
                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;
    }

這個方法主要是先為beanClass生成一個快取的key,這個beanClass如果是FactoryBean,則按照工廠類的命名規則命名,否則用beanName命名,然後用剛才生成的key判斷beanClass是否已經存在於Advice的快取集合中,如果已經存在則代表該類是切面類而且已經被處理過了,後續處理不會為該類生成代理,如果沒有沒處理過,則會調用下面的方法校驗該類是否是AOP的基礎類 ,總之這個方法作用就是將AOP相關操作的切面類和基礎類放入到快取中,當為bean生成代理的時候,忽略advice快取中的AOP切面類和基礎類,下面是具體校驗過程:
AnnotationAwareAspectJAutoProxyCreator重寫了該方法

@Override
protected boolean isInfrastructureClass(Class<?> beanClass) {
//調用父類的isInfrastructureClass判斷是否是aop基礎類
//校驗當前類是否使用@Aspect註解
return (super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass));
}

父類的isInfrastructureClass方法

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        boolean retVal = Advice.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;
    }

裡面isAssignableFrom表示當前類是否允許被設置為beanClass類對象,可以以此判斷beanClass是否是Advice類,所以這個方法的校驗目的就是判斷當前正在創建目標類是否是AOP的基礎類,即該類是否是Advice,Advisor或者實現了AopInfrastructureBean介面。該方法調用父類的isInfrastructureClass判斷是否是aop基礎類,然後再校驗當前類是否使用@Aspect註解,目的只有一個,如果是Advice切面相關的類不做任何處理,直接放入advice快取即可。
然後再來看shouldSkip(beanClass, beanName):

    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        //查找當前已經生成的所有Advisor切面類  不展開分析
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        for (Advisor advisor : candidateAdvisors) {
            if (advisor instanceof AspectJPointcutAdvisor) {
                if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
                    return true;
                }
            }
        }
        return super.shouldSkip(beanClass, beanName);
    }

這個方法主要是校驗當前正在創建bean的beanName是否屬於已經創建好的切面類快取中,如果是則加入到advices快取中,不再處理。其中findCandidateAdvisors()會查找當前容器中生成的所有實現了Advisor的類,Spring會將@Before,@After,@Around等生成一個繼承了Advisor類對象存儲到快取中供後續使用,這一部分時Spring AOP前半段的核心內容,後續都會圍繞著如何將切面類的註解生成Adisor類探索。
AnnotationAwareAspectJAutoProxyCreator重寫了findCandidateAdvisors方法,所以會執行到該方法:

@Override
protected List<Advisor> findCandidateAdvisors() {
   //通過父類的方法查找所有容器中的Advisor類,也就是基於xml配置的<aop:before/>生成的
   List<Advisor> advisors = super.findCandidateAdvisors();
   //查找通過註解的方式生成Advisor類
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}

這個方法會首先調用父類的findCandidateAdvisors方法用於獲取通過xml文件配置生成的Advisor,也就是通過aop:before,aop:after等生成的,然後調用通過註解方式即@Before,@After,@Around、@Pointcut,@AfterThrowing生成的advisor,可以說,這兩個方法分別處理了基於xml配置文件的方式和基於註解的配置方式,因為所有的分析都是基於AnnotationAwareAspectJAutoProxyCreator這個類進行的,所以在這個地方會先獲取配置文件的,再生成基於註解類的Advisor,這樣就將基於xml配置的和基於註解的配置都會解析到。
看下 this.aspectJAdvisorsBuilder.buildAspectJAdvisors()

    public List<Advisor> buildAspectJAdvisors() {
        List<String> aspectNames = null;
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new LinkedList<Advisor>();
                aspectNames = new LinkedList<String>();
                //從beanDefinitions中獲取所有的beanName
                String[] beanNames =
                        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                for (String beanName : beanNames) {
                    //如果beanName不符合配置的 <aop:include name="***"/>
                    //忽略這個bean上所有的切面方法
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    //如果當前beanType是一個切面類 則將該切面類相關資訊封裝起來
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            // 將切面資訊放入到分裝到MetadataAwareAspectInstanceFactory 生成一個AspectMetadata
                            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 {
                            // 非單例  將生成Advisor的工廠類加入到快取
                            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;
            }
        }
        ..............
    }

這個方法主要的任務其實就是獲取類得類型為Aspect的切面類,然後獲取切面類方法的所有註解並將註解轉換成Advisor類返回,主要步驟為:

  • 獲取容器中所有的BeanDefinition的beanName
  • 根據beanName,或者beanClass,匹配符合規則的Aspect切面類,通過aop:include配置的規則
  • 獲取Aspect切面類的所有切面方法封裝成Advisor對象返回。
  • 將獲取到的所有Advisor放入到快取中。
    這個方法程式碼雖然很多,但是核心的是this.advisorFactory.getAdvisors(factory),即第三個步驟,這個方法將會獲取到切面類的所有切面方法,並封裝成Advisor,getAdvisors是一個介面,ReflectiveAspectJAdvisorFactory實現了這個介面,下面程式碼是其實現邏輯:
@Override
    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        //獲取切面類Class
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        //獲取切面類的beanName
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        validate(aspectClass);
        //進一步對AspectMetadata封裝 裡面包含了切面類的資訊
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
        List<Advisor> advisors = new LinkedList<Advisor>();
        //獲取切面類中沒有使用Pointcut註解的方法
        for (Method method : getAdvisorMethods(aspectClass)) {
            //檢查該方法是否是切面方法, 如果是成Advisor類返回
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
        //如果沒有切面方法 設置一個空的
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }
        //處理屬性欄位 Spring支援到了屬性的增強
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
        return advisors;
    }

這個方法首先已經將切面類資訊封裝到AspectMetadata的類再次封裝到MetadataAwareAspectInstanceFactory,然後獲取切面類的所有沒有使用Pointcut註解的方法,調用getAdvisor獲取這個方法使用的切面註解,生成對應的Advisor類。 至於PointCut的處理則是再後面的getAdvisor中處理的。

9 獲取切面類的Advisor

獲取Advisor類的方法為getAdvisor,首先來看下這個方法的參數:

//切面類的切面方法  這裡可能就是 beforePrint()
Method  candidateAdviceMethod 
//獲取AspectMetadata的實例工廠(可以獲取切面的類所有資訊)
MetadataAwareAspectInstanceFactory aspectInstanceFactory
//切面的排序
int declarationOrderInAspect
//切面類的beanName 這裡是tracesRecordAdvisor
 String aspectName

上面的參數中可以獲取到切面類和切面方法,這樣就可以獲得一個Advisor對象,然後還需要一個切入點表達式PointCut用來匹配符合條件的方法,攔截到目標方法後,就可以執行Adivsor增強方法了。 來看看創建Advisor的過程,這裡假設Method是TracesRecordAdvisor類的beforePrint方法,也就是我們測試案例中創建使用了@Before註解的切面方法:

@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName) {

   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
   //獲取pointCut,這裡實際上獲得的是 expression()這個方法對應了pointCut的內容
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }
    //創建advisor
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

看看getPointCut方法如何獲取到exression過程需要嵌套很多步驟,這裡不展開了,簡單看下如何將查找到的值設置到表達式中的:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
   //
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }

   AspectJExpressionPointcut ajexp =
         new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
   //將上面生成的AspectJAnnotation 解析出的expression方法放入到表達式中
   //
   ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
   return ajexp;
}

這裡需要關注下上面的findAspectJAnnotationOnMethod方法:

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    //看到了我們熟悉的切面方法註解,這裡的beforePrint使用@Before註解
   Class<?>[] classesToLookFor = new Class<?>[] {
         Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
   for (Class<?> c : classesToLookFor) {
      AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
      if (foundAnnotation != null) {
         return foundAnnotation;
      }
   }
   return null;
}

這個方法就是查找切面方法是否使用了Before, Around, After,AfterReturning, AfterThrowing,Pointcut註解,如果使用了,則返回一個AspectJAnnotation對象,裡面有一個annotation的泛型對象,這個泛型對象就是被設置為這些註解的值,而且還會獲得這些註解裡面配置的pointcut表達式內容,如果是引用的表達式方法,則將方法參數設置到pointcutExpression這個屬性中。
解析完切面方法的註解後現在再回過頭來看看如何創建一個advisor實例:

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
      Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
      MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
   this.declaredPointcut = declaredPointcut;
   this.aspectJAdviceMethod = aspectJAdviceMethod;
   this.aspectJAdvisorFactory = aspectJAdvisorFactory;
   this.aspectInstanceFactory = aspectInstanceFactory;
   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);

      // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
      // If it's not a dynamic pointcut, it may be optimized out
      // by the Spring AOP infrastructure after the first evaluation.
      this.pointcut = new PerTargetInstantiationModelPointcut(
            this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
      this.lazy = true;
   }
   else {
      this.pointcut = this.declaredPointcut;
      this.lazy = false;
       //最終會執行到這裡獲取一個advice
      this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
   }
}

10 為切面方法創建Advice

上面方法的最後一句instantiateAdvice(this.declaredPointcut)會創建一個advice,具體是調用getAdvice方法獲取:

    @Override
    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
                            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        //獲取切面類對象,這裡是TracesRecordAdvisor
        Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        validate(candidateAspectClass);
        //核心點1:獲取切面註解,這裡得方法是 beforePrint 使用了@Before註解
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
            ....................
        AbstractAspectJAdvice springAdvice;
        //核心點2:根據註解轉換後的 將註解生成不同的Advice類。
        switch (aspectJAnnotation.getAnnotationType()) {
            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;
            case AtAround:
                springAdvice = new AspectJAroundAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtPointcut:
                //這裡對PointCut不做處理
                if (logger.isDebugEnabled()) {
                    logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }
                return null;
            default:
                throw new UnsupportedOperationException(
                        "Unsupported advice type on method: " + candidateAdviceMethod);
        }

        // 將切面類資訊配置到SpringAdvice中
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrder);
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            springAdvice.setArgumentNamesFromStringArray(argNames);
        }
        springAdvice.calculateArgumentBindings();
        return springAdvice;
    }

首先來看看核心點1,上面其實已經看過了, 但是上面的方法作用僅僅是為了獲取註解上的exression表達式的,這裡再調用一遍就是為註解生成Advice類的,目的就是獲取切面註解與AspectJAnnotation的映射類。

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    //看到了我們熟悉的切面方法註解,這裡的beforePrint使用@Before註解
   Class<?>[] classesToLookFor = new Class<?>[] {
         Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
   for (Class<?> c : classesToLookFor) {
      AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
      if (foundAnnotation != null) {
         return foundAnnotation;
      }
   }
   return null;
}

這個方法就是查找切面方法是否實現了Before, Around, After,AfterReturning, AfterThrowing,Pointcut註解,如果實現了,則返回一個AspectJAnnotation對象,裡面有一個annotation的泛型對象,這個泛型對象就是被設置為這些註解的值。最終這些對象會被轉換成下面的對象存入AspectJAnnotation中:

static {
   //會將註解轉換成後面的AspectJAnnotationType枚舉的類。 
   annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
   annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
   annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
   annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
   annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
   annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
}

通過核心點1,Spring已經將註解@Before對應轉換為AtBefore,@After轉換成AtAfter,以此類推,都會一一映射到了核心點2的switch的條件類了,在核心點2中,會為對應的切面註解類生成Advice類。 所有的註解切面類具體實現都是由AbstractAspectJAdvice這個抽象類實現的,這個類的構造函數有三個參數:

//切面方法 這裡可能是beforePrint
Method aspectJAroundAdviceMethod
//切入點表達式匹配器 這裡指封裝了exression的匹配器
AspectJExpressionPointcut pointcut
//切面類  這裡指TracesRecordAdvisor
AspectInstanceFactory aif

下面是Spring為對應註解生成對應的Advice類
|–|–|
|註解類|Advice 顧問方法|
|AtBefore|AspectJMethodBeforeAdvice|
|AtAfter|AspectJAfterAdvice|
|AtAfterReturning|AspectJAfterReturningAdvice|
|AtAfterThrowing|AspectJAfterThrowingAdvice|
|AtAround|AspectJAroundAdvice|

各個註解會在不同的實際執行自身增強方法,這個部分只是生成Advice類,然會放入到快取中,等真正生成代理時就會調用這些方法。這個在創建代理的時候需要具體拆開說,至此,Spring將使用了@Aspect註解的切面類的切面方法,都轉換成了對應的Adivsor類,這個類包含了切面方法,封裝後的切點匹配器PointCut以及生成切面類的實例對象,通過這個類就可以匹配到符合條件的目標類的目標方法,然後執行增強操作了。
由切面註解生成的Advice類,最終會放入到一個快取中,當生成目標bean的時候,會將所有所以能夠匹配到目標bean的advice放入到集合中,由一個實現了MethodInvocation的類統一管理調用過程,這個類後面會詳細說到,這裡簡單分析下AspectJAfterAdvice的invoke方法,看看它的調用過程

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
             //調用是實現了MethodInvocation方法的類 這個其實是個鏈式調用
			return mi.proceed();
		}
		finally {
            //最終執行後置增強方法
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}

上面的invoke方法需要一個MethodInvocation的參數,上面的Advice類除了AspectJMethodBeforeAdvice之外,都實現了這個介面,所以可以實現鏈式調用,這個邏輯會在創建代理的具體講解,這裡只是簡單分析下,這些advice的invoke方法規定了切面方法於要增強方法的執行時機。

11 初探AOP代理

上面一部分操作主要是處理使用了@Aspect註解的切面類,然後將切面類的所有切面方法根據使用的註解生成對應的Advisor的過程,這個Advisor包含了切面方法,切入點匹配器和切面類,也就是准好了要增強的邏輯,接下來就是要將這些邏輯注入到合適的位置進行增強,這部分的操作就是由老生常談的代理實現的了。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
       //如果要創建的類不是提前暴露的代理 則進入下面的方法
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

創建代理前,需要先校驗bean是否需要創建代理

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //如果bean是通過TargetSource介面獲取 則直接返回
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
    //如果bean是切面類 直接返回
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
    //如果bean是Aspect 而且允許跳過創建代理, 加入advise快取 返回
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }
   //如果前面生成的advisor快取中存在能夠匹配到目標類方法的Advisor 則創建代理
   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;
}

方法很簡單,主要的關注點在getAdvicesAndAdvisorsForBean和createProxy上,第一個是獲取能夠匹配目標類方法的Advisor集合,如果這個集合不為空,則代表該類需要被增強,需要生成代理,如果匹配不到,則表示該類並不需要被增強,無需創建代理。至於createProxy就很明顯了,就是創建代理,這個方法裡面決定了使用jdk代理還是cglib代理,並且用到了前面生成的Advisor實現增強功能。 這部分內容會放到下一篇文章中專門分析。

12 總結

簡單總結一下,Spring AOP在初始階段完成的主要任務:
讀取配置文件階段:

  • 讀取xml文件遇到 <aop:aspectj-autoproxy/>標籤時,找到命名空間處理器AopNamespaceHandler,然後找到處理該標籤的類AspectJAutoProxyBeanDefinitionParser
  • 通過AspectJAutoProxyBeanDefinitionParser的parse方法,將AspectJAwareAdvisorAutoProxyCreator註冊到容器的聲明周期中。
    創建bean階段:
  • 執行AspectJAwareAdvisorAutoProxyCreator的postProcessBeforeInstantiation校驗目標類是否是Aspect類和AOP基礎類以及是否需要跳過不需要執行代理的類
  • 獲取beanDefinitions中所有使用了Aspect註解的類,然後將切面方法根據使用的註解生成Advisor類放入到快取(關鍵)
  • 調用AspectJAwareAdvisorAutoProxyCreator的postProcessAfterInitialization的方法,對需要增強的類創建代理。