天天看點

Spring事務解析3-增強方法的擷取

從InfrastructureAdvisorAutoProxyCreator的層次結構中可以看到,InfrastructureAdvisorAutoProxyCreator間接實作了SmartInstantiationAwareBeanPostProcessor,而SmartInstantiationAwareBeanPostProcessor又繼承自InstantiationAwareBeanPostProcessor,也就是說在Spring中,所有bean執行個體化時Spring都會保證調用其postProcessAfterInitialization方法,其實作是在父類AbstractAutoProxyCreator類中實作。

這裡實作的主要目的是對指定bean進行封裝,當然首先要确定是否需要封裝,檢測及封裝的工作都委托給了wrapIfNecessary函數進行。

wrapIfNecessary函數功能實作起來很複雜,但是邏輯上了解起來還是相對簡單的,在wrapIfNecessary函數中主要的工作如下。

(1)找出指定bean對應的增強器。

(2)根據找出的增強器建立代理。

擷取指定bean對應的增強器,其中包含兩個關鍵字:增強器與對應。也就是說在getAdvicesAndAdvisorsForBean函數中,不但要找出增強器,而且還需要判斷增強器是否滿足要求。

漸漸地體會到了 Spring 中代碼的優秀,即使是一個很複雜的邏輯,在 Spring中也會被拆分成若幹個小的邏輯,然後在每個函數中實作,使得每個函數的邏輯簡單到我們能快速地了解,而不會像有些人開發的那樣,将一大堆的邏輯都羅列在一個函數中,給後期維護人員造成巨大的困擾。

同樣,通過上面的函數,Spring又将任務進行了拆分,分成了擷取所有增強器與增強器是否比對兩個功能點。

首先是通過BeanFactoryUtils類提供的工具方法擷取所有對應Advisor.class的類,擷取辦法無非是使用ListableBeanFactory中提供的方法:

String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit);

而當我們知道增強器在容器中的beanName時,擷取增強器已經不是問題了,在BeanFactory中提供了這樣的方法,可以幫助我們快速定位對應的bean執行個體。

T getBean(String name, Class requiredType) throws BeansException;

自定義标簽曾經注冊了一個類型為BeanFactoryTransactionAttributeSourceAdvisor的bean,而在此bean中我們又注入了另外兩個Bean,那麼此時這個Bean就會被開始使用了。因為BeanFactoryTransactionAttributeSourceAdvisor同樣也實作了Advisor接口,那麼在擷取所有增強器時自然也會将此bean提取出來,并随着其他增強器一起在後續的步驟中被織入代理。

BeanFactoryTransactionAttributeSourceAdvisor.java

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

  private TransactionAttributeSource transactionAttributeSource;

  private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {

    @Override

    protected TransactionAttributeSource getTransactionAttributeSource() {

      return transactionAttributeSource;

    }

  };

}

對于事務的配置不僅僅局限于在函數上配置,我們都知道,在類活接口上的配置可以延續到類中的每個函數,那麼,如果針對每個函數進行檢測,在類本身上配置的事務屬性豈不是檢測不到了嗎?帶着這個疑問,我們繼續探求matcher方法。做比對的時候methodMatcher.matches(method, targetClass)會使用TransactionAttributeSource Pointcut類的matches方法。

很遺憾,在getTransactionAttribute函數中并沒有找到我們想要的代碼,沒有找到在類活接口上的配置可以延續到類中的每個函數的邏輯,這裡是指正常的一貫的套路。嘗試從緩存加載,如果對應資訊沒有被緩存的話,工作又委托給了computeTransactionAttribute函數,在computeTransactionAttribute函數中終于的我們看到了事務标簽的提取過程。

如果方法中存在事務屬性,則使用方法上的屬性,否則使用方法所在的類上的屬性,如果方法所在類的屬性上還是沒有搜尋到對應的事務屬性,那麼再搜尋接口中的方法,再沒有的話,最後嘗試搜尋接口的類上面的聲明。對于函數computeTransactionAttribute中的邏輯與我們所認識的規則并無差别,但是上面函數中并沒有真正的去做搜尋事務屬性的邏輯,而是搭建了個執行架構,将搜尋事務屬性的任務委托給了findTransactionAttribute方法去執行。

上面方法中實作了對對應類或者方法的事務屬性解析,你會在這個類中看到任何你常用或者不常用的屬性提取。

找出某個增強器是否适合于對應的類,而是否比對的關鍵則在于是否從指定的類或類中的方法中找到對應的事務屬性,這個任務至此是完成了。在,我們以UserServiceImpl為例,已經在它的接口UserService中找到了事務屬性,是以,它是與事務增強器比對的,也就是它會被事務功能修飾。

當判斷某個bean适用于事務增強時,也就是适用于增強器BeanFactoryTransactionAttributeSourceAdvisor,然後執行最上面的return advisors.toArray();傳回,至此完成了Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);增強器的擷取。