Spring Aop之Advisor解析

       在上文Spring Aop之Target Source詳解中,咱們講解了Spring是如何經過封裝Target Source來達到對最終獲取的目標bean進行封裝的目的。其中咱們講解到,Spring Aop對目標bean進行代理是經過AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization()進行的,Spring Aop的代理主要分爲三個步驟:獲取全部的Advisor,過濾可應用到當前bean的Adivsor和使用Advisor爲當前bean生成代理對象。本文主要對這三步中的第一步獲取全部的Advisor進行講解。java

1. 骨架方法

       首先咱們看看postProcessAfterInitialization()方法的實現:express

public Object postProcessAfterInitialization(@Nullable Object bean, 
       String beanName) throws BeansException {
    if (bean != null) {
        // 獲取當前bean的key:若是beanName不爲空,則以beanName爲key,若是爲FactoryBean類型,
        // 前面還會添加&符號,若是beanName爲空,則以當前bean對應的class爲key
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 判斷當前bean是否正在被代理,若是正在被代理則不進行封裝
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 對當前bean進行封裝
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

       從上述代碼能夠看出,對目標bean的封裝是主要是經過wrapIfNecessary()方法進行的,該方法就是Spring對目標bean進行代理的骨架方法。以下是該方法的實現:數組

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 判斷當前bean是否在TargetSource緩存中存在,若是存在,則直接返回當前bean。這裏進行如此判斷的
    // 緣由是在上文中,咱們講解了如何經過本身聲明的TargetSource進行目標bean的封裝,在封裝以後其實
    // 就已經對封裝以後的bean進行了代理,而且添加到了targetSourcedBeans緩存中。於是這裏判斷獲得
    // 當前緩存中已經存在當前bean,則說明該bean已經被代理過,這樣就能夠直接返回當前bean。
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    
    // 這裏advisedBeans緩存了已經進行了代理的bean,若是緩存中存在,則能夠直接返回
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    
    // 這裏isInfrastructureClass()用於判斷當前bean是否爲Spring系統自帶的bean,自帶的bean是
    // 不用進行代理的;shouldSkip()則用於判斷當前bean是否應該被略過
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        // 對當前bean進行緩存
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 獲取當前bean的Advices和Advisors
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        // 對當前bean的代理狀態進行緩存
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 根據獲取到的Advices和Advisors爲當前bean生成代理對象
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, 
            new SingletonTargetSource(bean));
        // 緩存生成的代理bean的類型,而且返回生成的代理bean
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

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

       在上述骨架方法中,Spring主要進行了三件事:緩存

  • 判斷當前bean是否已經生成過代理對象,或者是不是應該被略過的對象,是則直接返回,不然進行下一步;
  • 獲取當前bean的Advisors和Advices,若是當前bean不須要代理,則返回DO_NOT_PROXY;
  • 經過生成的Advisors和Advices爲目標bean生成代理對象。

       關於上述骨架方法,這裏須要說明兩個點:post

  • shouldSkip()方法中對當前bean判斷是否應該略過期,其主要作了兩件事:a. 爲當前bean生成須要代理的Advisors;b. 判斷生成的Advisor是否爲AspectJPointcutAdvisor類型。於是實際上判斷略過的過程就是判斷是否爲AspectJPointcutAdvisor,判斷這個類的緣由在於Spring Aop的切面和切點的生成也能夠經過在xml文件中使用<aop:config/>標籤進行。這個標籤最終解析獲得的Adivsor類型就是``AspectJPointcutAdvisor類型的,由於其在解析aop:config/的時候就已經生成了Advisor,於是這裏須要對這種類型的Advisor進行略過。這裏aop:config/`也是一種自定義標籤,關於其解析過程,讀者能夠參照本人前面的博文自行閱讀器源碼;
  • getAdvicesAndAdvisorsForBean()方法就其名稱而言是獲取Advisors和Advices,但實際上其返回值是一個Advisor的數組。Spring Aop在爲目標bean獲取須要進行代理的切面邏輯的時候最終獲得的是Advisor,這裏Advice表示的是每一個切面邏輯中使用@Before@After@Around等須要織入的代理方法。由於每一個代理方法都表示一個Advice,而且每一個代理方法最終都會生成一個Advisor,於是Advice和Advisor就本質而言其實沒有太大的區別。Advice表示須要織入的切面邏輯,而Advisor則表示將切面邏輯進行封裝以後的織入者。

2. 切面的生成

       雖然在shouldSkip()方法中會爲當前bean生成Advisor,可是在getAdvicesAndAdvisorsForBean()中也仍是會獲取一次,只不過在第一次生成的時候會將獲得的Advisor都進行緩存,於是第二次獲取時能夠直接從緩存中獲取。咱們這裏仍是以getAdvicesAndAdvisorsForBean()方法爲準來進行講解。以下是該方法的源碼:優化

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, 
        @Nullable TargetSource targetSource) {
    // 爲目標bean生成Advisor
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

       咱們繼續看findEligibleAdvisors()方法:ui

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 將當前系統中全部的切面類的切面邏輯進行封裝,從而獲得目標Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 對獲取到的全部Advisor進行判斷,看其切面定義是否能夠應用到當前bean,從而獲得最終須要應用的Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, 
        beanClass, beanName);
    // 提供的hook方法,用於對目標Advisor進行擴展
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 對須要代理的Advisor按照必定的規則進行排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

       在上述方法中,Spring Aop首先獲取到了系統中全部的切面邏輯,並將其封裝爲了Advisor對象,而後經過遍歷Advisor判斷哪些Advisor是能夠應用到當前bean的,最後將須要織入的Advisor返回。這裏咱們看看findCandidateAdvisors()的源碼:this

protected List<Advisor> findCandidateAdvisors() {
    // 找到系統中實現了Advisor接口的bean
    List<Advisor> advisors = super.findCandidateAdvisors();
    if (this.aspectJAdvisorsBuilder != null) {
        // 找到系統中使用@Aspect標註的bean,而且找到該bean中使用@Before,@After等標註的方法,
        // 將這些方法封裝爲一個個Advisor
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

       能夠看到,findCandidateAdvisors()主要是經過兩種方式獲取切面邏輯,一種是在系統中找到實現了Advisor接口的全部類,另外一種是在找到系統中使用@Aspect標註的類,並將其切面邏輯封裝爲Advisor,這兩種Advisor都有多是咱們須要進行織入的切面邏輯。這裏super.findCandidateAdvisors()方法最終調用的是BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()方法,咱們首先看看該方法的實現:.net

public List<Advisor> findAdvisorBeans() {
    String[] advisorNames = null;
    synchronized (this) {
        advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            // 獲取當前BeanFactory中全部實現了Advisor接口的bean的名稱
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
    }
    if (advisorNames.length == 0) {
        return new LinkedList<>();
    }

    // 對獲取到的實現Advisor接口的bean的名稱進行遍歷
    List<Advisor> advisors = new LinkedList<>();
    for (String name : advisorNames) {
        // isEligibleBean()是提供的一個hook方法,用於子類對Advisor進行過濾,這裏默認返回值都是true
        if (isEligibleBean(name)) {
            // 若是當前bean還在建立過程當中,則略過,其建立完成以後會爲其判斷是否須要織入切面邏輯
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            } else {
                try {
                    // 將當前bean添加到結果中
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                } catch (BeanCreationException ex) {
                    // 對獲取過程當中產生的異常進行封裝
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        String bceBeanName = bce.getBeanName();
                        if (bceBeanName != null && 
                            this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Skipping advisor '" + name + 
                                    "' with dependency on currently created bean: " 
                                    + ex.getMessage());
                            }
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }
    return advisors;
}

       這裏findAdvisorBeans()方法邏輯其實很是簡單,其主要是在BeanFactory中找打實現了Advisor接口的類,而後經過hook方法判斷子類是否須要對Advisor進行過濾,最後將過濾以後的Advisor返回。debug

       接下來咱們看看BeanFactoryAspectJAdvisorsBuilder.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<>();
                aspectNames = new LinkedList<>();
                // 獲取當前BeanFactory中全部的bean
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Object.class, true, false);
                // 對獲取到的全部bean進行循環遍歷
                for (String beanName : beanNames) {
                    // 判斷當前bean是否爲子類定製的須要過濾的bean
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    
                    // 獲取當前遍歷的bean的Class類型
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    
                    // 判斷當前bean是否使用了@Aspect註解進行標註
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        
                        // 對於使用了@Aspect註解標註的bean,將其封裝爲一個AspectMetadata類型。
                        // 這裏在封裝的過程當中會解析@Aspect註解上的參數指定的切面類型,如perthis
                        // 和pertarget等。這些被解析的註解都會被封裝到其perClausePointcut屬性中
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        // 判斷@Aspect註解中標註的是否爲singleton類型,默認的切面類都是singleton
                        // 類型
                        if (amd.getAjType().getPerClause().getKind() == 
                            PerClauseKind.SINGLETON) {
                            // 將BeanFactory和當前bean封裝爲MetadataAwareAspect-
                            // InstanceFactory對象,這裏會再次將@Aspect註解中的參數都封裝
                            // 爲一個AspectMetadata,而且保存在該factory中
                            MetadataAwareAspectInstanceFactory factory =
                                new BeanFactoryAspectInstanceFactory(this.beanFactory, 
                                    beanName);
                            // 經過封裝的bean獲取其Advice,如@Before,@After等等,而且將這些
                            // Advice都解析而且封裝爲一個個的Advisor
                            List<Advisor> classAdvisors 
                                this.advisorFactory.getAdvisors(factory);
                            // 若是切面類是singleton類型,則將解析獲得的Advisor進行緩存,
                            // 不然將當前的factory進行緩存,以便再次獲取時能夠經過factory直接獲取
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            } else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);
                        } else {
                            // 若是@Aspect註解標註的是perthis和pertarget類型,說明當前切面
                            // 不多是單例的,於是這裏判斷其若是是單例的則拋出異常
                            if (this.beanFactory.isSingleton(beanName)) {
                                throw new IllegalArgumentException("Bean with name '" 
                                  + beanName + "' is a singleton, but aspect "
                                  + "instantiation model is not singleton");
                            }
                            
                            // 將當前BeanFactory和切面bean封裝爲一個多例類型的Factory
                            MetadataAwareAspectInstanceFactory factory =
                                new PrototypeAspectInstanceFactory(this.beanFactory, 
                                  beanName);
                            // 對當前bean和factory進行緩存
                            this.aspectFactoryCache.put(beanName, factory);
                            advisors.addAll(this.advisorFactory.getAdvisors(factory));
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    
    // 經過全部的aspectNames在緩存中獲取切面對應的Advisor,這裏若是是單例的,則直接從advisorsCache
    // 獲取,若是是多例類型的,則經過MetadataAwareAspectInstanceFactory當即生成一個
    List<Advisor> advisors = new LinkedList<>();
    for (String aspectName : aspectNames) {
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        // 若是是單例的Advisor bean,則直接添加到返回值列表中
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        } else {
            // 若是是多例的Advisor bean,則經過MetadataAwareAspectInstanceFactory生成
            MetadataAwareAspectInstanceFactory factory = 
                this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    return advisors;
}

       對於經過@Aspect註解獲取切面邏輯的方法,這裏的邏輯也比較簡單,Spring首先會過濾獲得BeanFactory中全部標註有@Aspect的類,而後對該註解參數進行解析,判斷其環繞的目標bean是單例的仍是多例的。若是是單例的,則直接緩存到advisorsCache中;若是是多例的,則將生成Advisor的factory進行緩存,以便每次獲取時都經過factory獲取一個新的Advisor。上述方法中主要是對@Aspect註解進行了解析,咱們前面講過,Spring Aop的Advisor對應的是Advice,而每一個Advice都是對應的一個@Before或者@After等標註方法的切面邏輯,這裏對這些切面邏輯的解析過程就在上述的advisorFactory.getAdvisors(factory)方法調用中。這裏咱們看看該方法的實現:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 獲取當前切面類的Class類型
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    // 獲取當前切面bean的名稱
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    // 對當前切面bean進行校驗,主要是判斷其切點是否爲perflow或者是percflowbelow,Spring暫時不支持
    // 這兩種類型的切點
    validate(aspectClass);

    // 將當前aspectInstanceFactory進行封裝,這裏LazySingletonAspectInstanceFactoryDecorator
    // 使用裝飾器模式,主要是對獲取到的切面實例進行了緩存,保證每次獲取到的都是同一個切面實例
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
        new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new LinkedList<>();
    // 這裏getAdvisorMethods()會獲取全部的沒有使用@Pointcut註解標註的方法,而後對其進行遍歷
    for (Method method : getAdvisorMethods(aspectClass)) {
        // 判斷當前方法是否標註有@Before,@After或@Around等註解,若是標註了,則將其封裝爲一個Advisor
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 
           advisors.size(), aspectName);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    // 這裏的isLazilyInstantiated()方法判斷的是當前bean是否應該被延遲初始化,其主要是判斷當前
    // 切面類是否爲perthis,pertarget或pertypewithiin等聲明的切面。由於這些類型所環繞的目標bean
    // 都是多例的,於是須要在運行時動態判斷目標bean是否須要環繞當前的切面邏輯
    if (!advisors.isEmpty() && 
        lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        // 若是Advisor不爲空,而且是須要延遲初始化的bean,則在第0位位置添加一個同步加強器,
        // 該同步加強器實際上就是一個BeforeAspect的Advisor
        Advisor instantiationAdvisor = new 
            SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    // 判斷屬性上是否包含有@DeclareParents註解標註的須要新添加的屬性,若是有,則將其封裝爲一個Advisor
    for (Field field : aspectClass.getDeclaredFields()) {
        Advisor advisor = getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    return advisors;
}

       在上述getAdvisors()方法中,Spring會遍歷當前切面類全部的方法,包括父類和父接口的方法,找到其中沒有使用@Pointcut註解標註的方法,而後對找到的方法進行遍歷,將其封裝爲一個Advisor。這裏咱們繼續看封裝爲Advisor的方法:

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

    // 校驗當前切面類是否使用了perflow或者percflowbelow標識的切點,Spring暫不支持這兩種切點
    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

    // 獲取當前方法中@Before,@After或者@Around等標註的註解,而且獲取該註解的值,將其
    // 封裝爲一個AspectJExpressionPointcut對象
    AspectJExpressionPointcut expressionPointcut = getPointcut(
       candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }

    // 將獲取到的切點,切點方法等信息封裝爲一個Advisor對象,也就是說當前Advisor包含有全部
    // 當前切面進行環繞所須要的信息
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, 
        candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, 
        aspectName);
}

       到這裏Spring纔將@Before,@After或@Around標註的方法封裝爲了一個Advisor對象。須要說明的是,這裏封裝成的Advisor對象只是一個半成品。所謂的半成品指的是此時其並無對切點表達式進行解析,其還只是使用一個字符串保存在AspectJExpressionPointcut對象中,只有在真正使用當前Advice邏輯進行目標bean的環繞的時候纔會對其進行解析。

3. 小結

       本文主要講解了Spring是如何獲取全部的Advisor的,即首先獲取BeanFactory中全部實現了Advisor接口的bean,而後獲取BeanFactory中全部標註了@Aspect註解的bean,解析該bean中的全部的切面邏輯,而且封裝爲一個個Advisor,這兩種方式獲得的Advisor都有多是最終會應用到目標bean上的切面邏輯。須要注意的是,這裏獲取到的Advisor並無對切點表達式進行解析,實際的解析過程是在判斷當前bean是否能夠應用到目標bean時進行的。這也是一個小小的優化,由於解析切點表達式的過程是一個比較複雜的過程。

相關文章
相關標籤/搜索