spring-aop 2.篩選通知器

1.前言

從本篇文章開(kāi)始,我將會(huì)對(duì) Spring AOP 部分的源碼進(jìn)行分析。本文是 Spring AOP 源碼分析系列文章的第二篇,本文主要分析 Spring AOP 是如何為目標(biāo) bean篩選出合適的通知器(Advisor)。

2.源碼分析

2.1AOP入口分析

在導(dǎo)讀一文中,我已經(jīng)說(shuō)過(guò) Spring AOP 是在何處向目標(biāo) bean 中織入通知(Advice)的。也說(shuō)過(guò) Spring 是如何將 AOP 和 IOC 模塊整合到一起的,即通過(guò)拓展點(diǎn) BeanPostProcessor 接口。Spring AOP 抽象代理創(chuàng)建器實(shí)現(xiàn)了 BeanPostProcessor 接口,并在 bean 初始化后置處理過(guò)程中向 bean 中織入通知。下面我們就來(lái)看看相關(guān)源碼,如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    
    @Override
    /** bean 初始化后置處理方法 */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // 如果需要,為 bean 生成代理對(duì)象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }

        /*
         * 如果是基礎(chǔ)設(shè)施類(lèi)(Pointcut、Advice、Advisor 等接口的實(shí)現(xiàn)類(lèi)),或是應(yīng)該跳過(guò)的類(lèi),
         * 則不應(yīng)該生成代理,此時(shí)直接返回 bean
         */ 
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            // 將 <cacheKey, FALSE> 鍵值對(duì)放入緩存中,供上面的 if 分支使用
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 為目標(biāo) bean 查找合適的通知器
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        /*
         * 若 specificInterceptors != null,即 specificInterceptors != DO_NOT_PROXY,
         * 則為 bean 生成代理對(duì)象,否則直接返回 bean
         */ 
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 創(chuàng)建代理
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            /*
             * 返回代理對(duì)象,此時(shí) IOC 容器輸入 bean,得到 proxy。此時(shí),
             * beanName 對(duì)應(yīng)的 bean 是代理對(duì)象,而非原始的 bean
             */ 
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        // specificInterceptors = null,直接返回 bean
        return bean;
    }
}

以上就是 Spring AOP 創(chuàng)建代理對(duì)象的入口方法分析,過(guò)程比較簡(jiǎn)單,這里簡(jiǎn)單總結(jié)一下:
1.若 bean 是 AOP 基礎(chǔ)設(shè)施類(lèi)型,則直接返回
2.為 bean 查找合適的通知器
3.如果通知器數(shù)組不為空,則為 bean 生成代理對(duì)象,并返回該對(duì)象
4.若數(shù)組為空,則返回原始 bean
上面的流程看起來(lái)并不復(fù)雜,不過(guò)不要被表象所迷糊,以上流程不過(guò)是冰山一角。

2.2篩選合適的通知器

在向目標(biāo) bean 中織入通知之前,我們先要為 bean 篩選出合適的通知器(通知器持有通知)。如何篩選呢?方式由很多,比如我們可以通過(guò)正則表達(dá)式匹配方法名,當(dāng)然更多的時(shí)候用的是 AspectJ 表達(dá)式進(jìn)行匹配。那下面我們就來(lái)看一下使用 AspectJ 表達(dá)式篩選通知器的過(guò)程,如下:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
    // 查找合適的通知器
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 查找所有的通知器
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    /*
     * 篩選可應(yīng)用在 beanClass 上的 Advisor,通過(guò) ClassFilter 和 MethodMatcher
     * 對(duì)目標(biāo)類(lèi)和方法進(jìn)行匹配
     */
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 拓展操作
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

如上,Spring 先查詢(xún)出所有的通知器,然后再調(diào)用 findAdvisorsThatCanApply 對(duì)通知器進(jìn)行篩選。在下面幾節(jié)中,我將分別對(duì) findCandidateAdvisors 和 findAdvisorsThatCanApply 兩個(gè)方法進(jìn)行分析,繼續(xù)往下看吧。

2.2.1 兩種配置方式

Spring 提供了兩種配置 AOP 的方式,一種是通過(guò) XML 進(jìn)行配置,另一種是注解。對(duì)于兩種配置方式,Spring 的處理邏輯是不同的。對(duì)于 XML 類(lèi)型的配置,比如下面的配置:

<!-- 目標(biāo) bean -->
<bean id="hello" class="xyz.coolblog.aop.Hello"/>

<aop:aspectj-autoproxy/>
    
<!-- 普通 bean,包含 AOP 切面邏輯 -->
<bean id="aopCode" class="xyz.coolblog.aop.AopCode"/>
<!-- 由 @Aspect 注解修飾的切面類(lèi) -->
<bean id="annotationAopCode" class="xyz.coolblog.aop.AnnotationAopCode"/>

<aop:config>
    <aop:aspect ref="aopCode">
        <aop:pointcut id="helloPointcut" expression="execution(* xyz.coolblog.aop.*.hello*(..))" />
        <aop:before method="before" pointcut-ref="helloPointcut"/>
        <aop:after method="after" pointcut-ref="helloPointcut"/>
    </aop:aspect>
</aop:config>

Spring 會(huì)將上的配置解析為下面的結(jié)果:


image.png

如上圖所示,紅框中對(duì)應(yīng)的是普通的 bean 定義,比如 <bean id="hello" .../>、<bean id="annotationAopCode" .../>、<bean id="appCode" .../> 等配置。黃色框中的則是切點(diǎn)的定義,類(lèi)型為 AspectJExpressionPointcut,對(duì)應(yīng) <aop:pointcut id="helloPointcut" .../> 配置。那綠色框中的結(jié)果對(duì)應(yīng)的是什么配置呢?目前僅剩下兩個(gè)配置沒(méi)說(shuō),所以對(duì)應(yīng) <aop:before .../> 和 <aop:after .../> 配置,類(lèi)型為 AspectJPointcutAdvisor。這里請(qǐng)大家注意,由 @Aspect 注解修飾的 AnnotationAopCode 也是普通類(lèi)型的 bean,該 bean 會(huì)在查找通知器的過(guò)程中被解析,并被構(gòu)建為一個(gè)或多個(gè) Advisor。

上面講解了 Spring AOP 兩種配置的處理方式,算是為下面的源碼分析做鋪墊?,F(xiàn)在鋪墊完畢,我們就來(lái)分析一下源碼吧。如下:

2.2.2 findCandidateAdvisors 方法分析(xml配置方式獲取)

我們先來(lái)看一下 AbstractAdvisorAutoProxyCreator 中 findCandidateAdvisors 方法的定義,如下:

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

    private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;
    
    //...

    protected List<Advisor> findCandidateAdvisors() {
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

    //...
}

從上面的源碼中可以看出,AbstractAdvisorAutoProxyCreator 中的 findCandidateAdvisors 是個(gè)空殼方法,所有邏輯封裝在了一個(gè) BeanFactoryAdvisorRetrievalHelper 的 findAdvisorBeans 方法中。這里大家可以仔細(xì)看一下類(lèi)名 BeanFactoryAdvisorRetrievalHelper 和方法 findAdvisorBeans,兩個(gè)名字其實(shí)已經(jīng)描述出他們的職責(zé)了。BeanFactoryAdvisorRetrievalHelper 可以理解為從 bean 容器中獲取 Advisor 的幫助類(lèi),findAdvisorBeans 則可理解為查找 Advisor 類(lèi)型的 bean。所以即使不看 findAdvisorBeans 方法的源碼,我們也可從方法名上推斷出它要做什么,即從 bean 容器中將 Advisor 類(lèi)型的 bean 查找出來(lái)。下面我來(lái)分析一下這個(gè)方法的源碼,如下:

public List<Advisor> findAdvisorBeans() {
    String[] advisorNames = null;
    synchronized (this) {
        // cachedAdvisorBeanNames 是 advisor 名稱(chēng)的緩存
        advisorNames = this.cachedAdvisorBeanNames;
        /*
         * 如果 cachedAdvisorBeanNames 為空,這里到容器中查找,
         * 并設(shè)置緩存,后續(xù)直接使用緩存即可
         */ 
        if (advisorNames == null) {
            // 從容器中查找 Advisor 類(lèi)型 bean 的名稱(chēng)
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            // 設(shè)置緩存
            this.cachedAdvisorBeanNames = advisorNames;
        }
    }
    if (advisorNames.length == 0) {
        return new LinkedList<Advisor>();
    }

    List<Advisor> advisors = new LinkedList<Advisor>();
    // 遍歷 advisorNames
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            // 忽略正在創(chuàng)建中的 advisor bean
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    /*
                     * 調(diào)用 getBean 方法從容器中獲取名稱(chēng)為 name 的 bean,
                     * 并將 bean 添加到 advisors 中
                     */ 
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Skipping advisor '" + name +
                                        "' with dependency on currently created bean: " + ex.getMessage());
                            }
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }

    return advisors;
}

以上就是從容器中查找 Advisor 類(lèi)型的 bean 所有的邏輯,代碼雖然有點(diǎn)長(zhǎng),但并不復(fù)雜。主要做了兩件事情:
1.從容器中查找所有類(lèi)型為 Advisor 的 bean 對(duì)應(yīng)的名稱(chēng)
2.遍歷 advisorNames,并從容器中獲取對(duì)應(yīng)的 bean
看完上面的分析,我們繼續(xù)來(lái)分析一下 @Aspect 注解的解析過(guò)程。

2.2.3 buildAspectJAdvisors 方法分析(注解方式獲?。?/h3>

AnnotationAwareAspectJAutoProxyCreator 覆寫(xiě)了父類(lèi)的方法 findCandidateAdvisors,并增加了一步操作,即解析 @Aspect 注解,并構(gòu)建成通知器。下面我先來(lái)分析一下父類(lèi)中的 findCandidateAdvisors 方法的邏輯,然后再來(lái)分析 buildAspectJAdvisors 方法邏的輯

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

    //...
    @Override
    protected List<Advisor> findCandidateAdvisors() {
        // 調(diào)用父類(lèi)方法從容器中查找所有的通知器
        List<Advisor> advisors = super.findCandidateAdvisors();
        // 解析 @Aspect 注解,并構(gòu)建通知器
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        return advisors;
    }
    //...
}

與上一節(jié)的內(nèi)容相比,解析 @Aspect 注解的過(guò)程還是比較復(fù)雜的,需要一些耐心去看。下面我們開(kāi)始分析 buildAspectJAdvisors 方法的源碼,如下:

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;

    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new LinkedList<Advisor>();
                aspectNames = new LinkedList<String>();
                // 從容器中獲取所有 bean 的名稱(chēng)
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // 遍歷 beanNames
                for (String beanName : beanNames) {
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    
                    // 根據(jù) beanName 獲取 bean 的類(lèi)型
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }

                    // 檢測(cè) beanType 是否包含 Aspect 注解
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            MetadataAwareAspectInstanceFactory factory =
                                    new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

                            // 獲取通知器
                            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 {
                            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 LinkedList<Advisor>();
    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;
}

上面就是 buildAspectJAdvisors 的代碼,看起來(lái)比較長(zhǎng)。代碼比較多,我們關(guān)注重點(diǎn)的方法調(diào)用即可。在進(jìn)行后續(xù)的分析前,這里先對(duì) buildAspectJAdvisors 方法的執(zhí)行流程做個(gè)總結(jié)。如下:
1.獲取容器中所有 bean 的名稱(chēng)(beanName)
遍歷上一步獲取到的 bean 名稱(chēng)數(shù)組,并獲取當(dāng)前 beanName 對(duì)應(yīng)的 bean 類(lèi)型(beanType)
根據(jù) beanType 判斷當(dāng)前 bean 是否是一個(gè)的 Aspect 注解類(lèi),若不是則不做任何處理
調(diào)用 advisorFactory.getAdvisors 獲取通知器

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 獲取 aspectClass 和 aspectName
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);

    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new LinkedList<Advisor>();

    // getAdvisorMethods 用于返回不包含 @Pointcut 注解的方法
    for (Method method : getAdvisorMethods(aspectClass)) {
        // 為每個(gè)方法分別調(diào)用 getAdvisor 方法
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), 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;
}

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

    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

    // 獲取切點(diǎn)實(shí)現(xiàn)類(lèi)
    AspectJExpressionPointcut expressionPointcut = getPointcut(
            candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }

    // 創(chuàng)建 Advisor 實(shí)現(xiàn)類(lèi)
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

如上,getAdvisor 方法包含兩個(gè)主要步驟,一個(gè)是獲取 AspectJ 表達(dá)式切點(diǎn),另一個(gè)是創(chuàng)建 Advisor 實(shí)現(xiàn)類(lèi)。在第二個(gè)步驟中,包含一個(gè)隱藏步驟 – 創(chuàng)建 Advice。下面我將按順序依次分析這兩個(gè)步驟,先看獲取 AspectJ 表達(dá)式切點(diǎn)的過(guò)程,如下:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 獲取方法上的 AspectJ 相關(guān)注解,包括 @Before,@After 等
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // 創(chuàng)建一個(gè) AspectJExpressionPointcut 對(duì)象
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    // 設(shè)置切點(diǎn)表達(dá)式
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    ajexp.setBeanFactory(this.beanFactory);
    return ajexp;
}

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    // classesToLookFor 中的元素是大家熟悉的
    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;
}

獲取切點(diǎn)的過(guò)程并不復(fù)雜,不過(guò)需要注意的是,目前獲取到的切點(diǎn)可能還只是個(gè)半成品,需要再次處理一下才行。比如下面的代碼:

@Aspect
public class AnnotationAopCode {

    @Pointcut("execution(* xyz.coolblog.aop.*.world*(..))")
    public void pointcut() {}

    @Before("pointcut()")
    public void before() {
        System.out.println("AnnotationAopCode`s before");
    }
}

@Before 注解中的表達(dá)式是pointcut(),也就是說(shuō) ajexp 設(shè)置的表達(dá)式只是一個(gè)中間值,不是最終值,即execution(* xyz.coolblog.aop..world(..))。所以后續(xù)還需要將 ajexp 中的表達(dá)式進(jìn)行轉(zhuǎn)換,關(guān)于這個(gè)轉(zhuǎn)換的過(guò)程,我就不說(shuō)了。有點(diǎn)復(fù)雜,我暫時(shí)沒(méi)怎么看懂。

說(shuō)完切點(diǎn)的獲取過(guò)程,下面再來(lái)看看 Advisor 實(shí)現(xiàn)類(lèi)的創(chuàng)建過(guò)程。如下:

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;
    this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    this.aspectInstanceFactory = aspectInstanceFactory;
    this.declarationOrder = declarationOrder;
    this.aspectName = aspectName;

    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Pointcut preInstantiationPointcut = Pointcuts.union(
                aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

        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);
    }
}

上面是 InstantiationModelAwarePointcutAdvisorImpl 的構(gòu)造方法,不過(guò)我們無(wú)需太關(guān)心這個(gè)方法中的一些初始化邏輯。我們把目光移到構(gòu)造方法的最后一行代碼中,即 instantiateAdvice(this.declaredPointcut),這個(gè)方法用于創(chuàng)建通知 Advice。在上一篇文章中我已經(jīng)說(shuō)過(guò),通知器 Advisor 是通知 Advice 的持有者,所以在 Advisor 實(shí)現(xiàn)類(lèi)的構(gòu)造方法中創(chuàng)建通知也是合適的。那下面我們就來(lái)看看構(gòu)建通知的過(guò)程是怎樣的,如下:

private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
            this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    validate(candidateAspectClass);

    // 獲取 Advice 注解
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    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;

    // 按照注解類(lèi)型生成相應(yīng)的 Advice 實(shí)現(xiàn)類(lèi)
    switch (aspectJAnnotation.getAnnotationType()) {
        case AtBefore:    // @Before -> AspectJMethodBeforeAdvice
            springAdvice = new AspectJMethodBeforeAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        case AtAfter:    // @After -> AspectJAfterAdvice
            springAdvice = new AspectJAfterAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        case AtAfterReturning:    // @AfterReturning -> AspectJAfterAdvice
            springAdvice = new AspectJAfterReturningAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;

        case AtAfterThrowing:    // @AfterThrowing -> AspectJAfterThrowingAdvice
            springAdvice = new AspectJAfterThrowingAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;

        case AtAround:    // @Around -> AspectJAroundAdvice
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        /*
         * 什么都不做,直接返回 null。從整個(gè)方法的調(diào)用棧來(lái)看,
         * 并不會(huì)出現(xiàn)注解類(lèi)型為 AtPointcut 的情況
         */ 
        case AtPointcut:    
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
            
        default:
            throw new UnsupportedOperationException(
                    "Unsupported advice type on method: " + candidateAdviceMethod);
    }

    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    /*
     * 獲取方法的參數(shù)列表名稱(chēng),比如方法 int sum(int numX, int numY), 
     * getParameterNames(sum) 得到 argNames = [numX, numY]
     */
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        // 設(shè)置參數(shù)名
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();
    return springAdvice;
}

上面的代碼邏輯不是很復(fù)雜,主要的邏輯就是根據(jù)注解類(lèi)型生成與之對(duì)應(yīng)的通知對(duì)象。下面來(lái)總結(jié)一下獲取通知器(getAdvisors)整個(gè)過(guò)程的邏輯,如下:

1.從目標(biāo) bean 中獲取不包含 Pointcut 注解的方法列表
2.遍歷上一步獲取的方法列表,并調(diào)用 getAdvisor 獲取當(dāng)前方法對(duì)應(yīng)的 Advisor
3.創(chuàng)建 AspectJExpressionPointcut 對(duì)象,并從方法中的注解中獲取表達(dá)式,最后設(shè)置到切點(diǎn)對(duì)象中
4.創(chuàng)建 Advisor 實(shí)現(xiàn)類(lèi)對(duì)象 InstantiationModelAwarePointcutAdvisorImpl
5.調(diào)用 instantiateAdvice 方法構(gòu)建通知
6.調(diào)用 getAdvice 方法,并根據(jù)注解類(lèi)型創(chuàng)建相應(yīng)的通知
如上所示,上面的步驟做了一定的簡(jiǎn)化??偟膩?lái)說(shuō),獲取通知器的過(guò)程還是比較復(fù)雜的,并不是很容易看懂。大家在閱讀的過(guò)程中,還要寫(xiě)一些測(cè)試代碼進(jìn)行調(diào)試才行。調(diào)試的過(guò)程中,一些不關(guān)心的調(diào)用就別跟進(jìn)去了,不然會(huì)陷入很深的調(diào)用棧中,影響對(duì)源碼主流程的理解。

現(xiàn)在,大家知道了通知是怎么創(chuàng)建的。那我們難道不要去看看這些通知的實(shí)現(xiàn)源碼嗎?顯然,我們應(yīng)該看一下。那接下里,我們一起來(lái)分析一下 AspectJMethodBeforeAdvice,也就是 @Before 注解對(duì)應(yīng)的通知實(shí)現(xiàn)類(lèi)。看看它的邏輯是什么樣的。

2.3 AspectJMethodBeforeAdvice 分析

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {

    public AspectJMethodBeforeAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }


    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        // 調(diào)用通知方法
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }

    @Override
    public boolean isBeforeAdvice() {
        return true;
    }

    @Override
    public boolean isAfterAdvice() {
        return false;
    }

}

protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
    // 調(diào)用通知方法,并向其傳遞參數(shù)
    return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        // 通過(guò)反射調(diào)用通知方法
        return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    }
    catch (IllegalArgumentException ex) {
        throw new AopInvocationException("Mismatch on arguments to advice method [" +
                this.aspectJAdviceMethod + "]; pointcut expression [" +
                this.pointcut.getPointcutExpression() + "]", ex);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    }
}

如上,AspectJMethodBeforeAdvice 的源碼比較簡(jiǎn)單,這里我們僅關(guān)注 before 方法。這個(gè)方法調(diào)用了父類(lèi)中的 invokeAdviceMethod,然后 invokeAdviceMethod 在調(diào)用 invokeAdviceMethodWithGivenArgs,最后在 invokeAdviceMethodWithGivenArgs 通過(guò)反射執(zhí)行通知方法。

2.4 篩選通知器

查找出所有的通知器,整個(gè)流程還沒(méi)算完,接下來(lái)我們還要對(duì)這些通知器進(jìn)行篩選。適合應(yīng)用在當(dāng)前 bean 上的通知器留下,不適合的就讓它自生自滅吧。那下面我們來(lái)分析一下通知器篩選的過(guò)程,如下:

protected List<Advisor> findAdvisorsThatCanApply(
        List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        // 調(diào)用重載方法
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
        // 篩選 IntroductionAdvisor 類(lèi)型的通知器
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            continue;
        }

        // 篩選普通類(lèi)型的通知器
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        /*
         * 從通知器中獲取類(lèi)型過(guò)濾器 ClassFilter,并調(diào)用 matchers 方法進(jìn)行匹配。
         * ClassFilter 接口的實(shí)現(xiàn)類(lèi) AspectJExpressionPointcut 為例,該類(lèi)的
         * 匹配工作由 AspectJ 表達(dá)式解析器負(fù)責(zé),具體匹配細(xì)節(jié)這個(gè)就沒(méi)法分析了,我
         * AspectJ 表達(dá)式的工作流程不是很熟
         */
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        // 對(duì)于普通類(lèi)型的通知器,這里繼續(xù)調(diào)用重載方法進(jìn)行篩選
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        return true;
    }

以上是通知器篩選的過(guò)程,篩選的工作主要由 ClassFilter 和 MethodMatcher 完成。在 AOP 中,切點(diǎn) Pointcut 是用來(lái)匹配連接點(diǎn)的,以 AspectJExpressionPointcut 類(lèi)型的切點(diǎn)為例。該類(lèi)型切點(diǎn)實(shí)現(xiàn)了ClassFilter 和 MethodMatcher 接口,匹配的工作則是由 AspectJ 表達(dá)式解析器復(fù)雜。除了使用 AspectJ 表達(dá)式進(jìn)行匹配,Spring 還提供了基于正則表達(dá)式的切點(diǎn)類(lèi),以及更簡(jiǎn)單的根據(jù)方法名進(jìn)行匹配的切點(diǎn)類(lèi)。大家有興趣的話(huà),可以自己去了解一下,這里就不多說(shuō)了。

在完成通知器的查找和篩選過(guò)程后,還需要進(jìn)行最后一步處理 – 對(duì)通知器列表進(jìn)行拓展。怎么拓展呢?我們一起到下一節(jié)中一探究竟吧。

2.5 拓展篩選出通知器列表

拓展方法 extendAdvisors 做的事情并不多,邏輯也比較簡(jiǎn)單。我們一起來(lái)看一下,如下:

protected void extendAdvisors(List<Advisor> candidateAdvisors) {
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // 如果通知器列表是一個(gè)空列表,則啥都不做
    if (!advisors.isEmpty()) {
        boolean foundAspectJAdvice = false;
        /*
         * 下面的 for 循環(huán)用于檢測(cè) advisors 列表中是否存在 
         * AspectJ 類(lèi)型的 Advisor 或 Advice
         */
        for (Advisor advisor : advisors) {
            if (isAspectJAdvice(advisor)) {
                foundAspectJAdvice = true;
            }
        }

        /*
         * 向 advisors 列表的首部添加 DefaultPointcutAdvisor,
         * 至于為什么這樣做,我會(huì)在后續(xù)的文章中進(jìn)行說(shuō)明
         */
        if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
            advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
            return true;
        }
    }
    return false;
}

private static boolean isAspectJAdvice(Advisor advisor) {
    return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
            advisor.getAdvice() instanceof AbstractAspectJAdvice ||
            (advisor instanceof PointcutAdvisor &&
                     ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}

如上,上面的代碼比較少,也不復(fù)雜。由源碼可以看出 extendAdvisors 是一個(gè)空殼方法,除了調(diào)用makeAdvisorChainAspectJCapableIfNecessary,該方法沒(méi)有其他更多的邏輯了。至于 makeAdvisorChainAspectJCapableIfNecessary 這個(gè)方法,該方法主要的目的是向通知器列表首部添加 DefaultPointcutAdvisor 類(lèi)型的通知器,也就是 ExposeInvocationInterceptor.ADVISOR。至于添加此種類(lèi)型通知器的意圖,我會(huì)在后面文章里說(shuō)明,這里不便展開(kāi)。關(guān)于 extendAdvisors 這個(gè)方法,這里就先說(shuō)到這了。

3.總結(jié)

到這里,本篇文章就接近尾聲了。這篇文章有點(diǎn)長(zhǎng),大家看下來(lái)應(yīng)該蠻累的吧。由于個(gè)人能力問(wèn)題,暫時(shí)未能做到對(duì)本篇文章中所貼的源碼進(jìn)行更為細(xì)致的分析,有點(diǎn)遺憾。不過(guò)好在目前把主邏輯分析弄清楚了,總的來(lái)說(shuō)還算合格吧,給個(gè)及格分。大家在閱讀的過(guò)程中,如果發(fā)現(xiàn)文章中出現(xiàn)錯(cuò)誤或不妥之處,這里還請(qǐng)指明,也請(qǐng)多多指教。大家共同學(xué)習(xí),一起進(jìn)步。

好了,本篇文章就到這里了。謝謝大家的閱讀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容