Spring篇:AspectJ工作原理分析

AspectJ的開啟

在Spring項目中通過AspectJ來使用aop時,需要在配置文件中做如下配置

<aop:aspectj-autoproxy/>

Spring對AspectJ標簽的解析

public class AopNamespaceHandler extends NamespaceHandlerSupport {

    /**
     * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
     * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
     * and '{@code scoped-proxy}' tags.
     */
    @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());
    }

}

可以看到Spring對aspectj標簽的解析是通過AspectJAutoProxyBeanDefinitionParser這個BeanDefinitionParser來完成的.
進入該類的parse方法,可以看到如下調(diào)用鏈



一直跟進到最內(nèi)層的方法查看代碼邏輯

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            return null;
        }
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
        return beanDefinition;
    }

這里便是AspectJAutoProxyBeanDefinitionParser完成的最主要的功能,即想容器注冊一個內(nèi)部BeanDefinition,該BeanDefinition的Class類型為AnnotationAwareAspectJAutoProxyCreator.class,這點從該函數(shù)調(diào)用層級的上一層傳進來的參數(shù)可以得到.

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

至此,完成了aspectj標簽的解析工作,注意,這里上述分析只是提到了解析這部分的核心部分,其他一些細節(jié)點就不再具體分析了.完成了解析工作只是為aspectj實現(xiàn)的aop做好了準備工作,下面在分析一下aop是如何運轉(zhuǎn)起來的.

AspectJ下aop的工作機制

通過前面的分析,發(fā)現(xiàn)AspectJAutoProxyBeanDefinitionParser注冊了一個AnnotationAwareAspectJAutoProxyCreator類型的內(nèi)部BeanDefinition,先來看一下這個類的繼承體系圖



可以看到AnnotationAwareAspectJAutoProxyCreator是BeanPostProcessor接口的一個實現(xiàn)類,那么它必然會實現(xiàn)了該接口中的postProcessAfterInitialization這個方法,而這里就是aop運行起來的核心起點.
AnnotationAwareAspectJAutoProxyCreator類中的postProcessAfterInitialization方法繼承自父類AbstractAutoProxyCreator,跟進方法可以看到其核心邏輯為wrapIfNecessary方法.來看下代碼實現(xiàn).

/**
     * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
     * @param bean the raw bean instance
     * @param beanName the name of the bean
     * @param cacheKey the cache key for metadata access
     * @return a proxy wrapping the bean, or the raw bean instance as-is
     */
    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;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        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;
    }

這里可以看到其核心思想就如下:

  • 獲取當前bean所適用的攔截器列表
  • 如果攔截器列表為空,說明當前bean不需要進行aop增強,那么直接返回原始bean;
  • 如果攔截器列表不為空,說明當前bean需要進行aop增強,那么為它生成代理對象返回,生成代理對象的方式無非就是jdk proxy或者是cglib proxy兩種方式,根據(jù)bean的情況具體判斷.

所以,這里的核心實現(xiàn)就是攔截器列表的獲取,即getAdvicesAndAdvisorsForBean方法.繼續(xù)跟進代碼:

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) {
        //  關(guān)鍵點1
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //  關(guān)鍵點2
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

上面代碼中關(guān)鍵點1處方法findCandidateAdvisors的作用是獲取所有的通知器.關(guān)鍵點2處方法findAdvisorsThatCanApply的作用是從通知器全量中找出適用于當前bean的.

先看一下findCandidateAdvisors方法內(nèi)部實現(xiàn)

@Override
    protected List<Advisor> findCandidateAdvisors() {
        // Add all the Spring advisors found according to superclass rules.
        //  這里是加載Spring中所有符合父類規(guī)則的通知器
        List<Advisor> advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        //  這里添加通過AspectJ注解配置的通知器,內(nèi)部實現(xiàn)包括了對@After @Before @Aroud等的解析工作
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        return advisors;
    }

這個方法中的兩個部分所完成的功能如注釋所述,就不再貼代碼.

findAdvisorsThatCanApply方法的大體邏輯就是判斷bean對應的class中的各方法是否有和通知器中的切點有匹配的,若匹配則記錄到一個可用列表中,若不匹配則過濾掉,然后返回該可用列表.這里需要注意的是該class中的方法只要有一個與切點匹配,即認為匹配成功,并不是要求所有方法都匹配.

至此,變獲得了最開始處wrapIfNecessary方法中需要的攔截器列表.前面的分析只描述的核心流程,其他一些細節(jié)點,比如對攔截器鏈的排序,一些緩存的使用以提高性能避免重復勞動還有一些擴展點就不再具體分析了.

后面便是根據(jù)攔截器列表生成所需的代理實例了,后面對原始bean中方法的調(diào)用會轉(zhuǎn)為對proxy對象中方法的調(diào)用,就是我們熟知的jdk proxy或者cglib proxy那一套原理了,當然具體的代理調(diào)用的實現(xiàn)細節(jié)會有不同,Spring做了自己的一些處理,但是核心思想和根本原理是一致的,此處就就不再做過多分析了.

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

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

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