fescar源碼解析系列(一)之啓動詳解

  fescar是gts剛開源的版本,對gts關注已久,比較熟悉其原理,而半年前本身又開發了一個可用版本meepo(詳情),因此對fescar的源碼也是必看。經過比較,能夠看meepo設計上的不足,以及一些編碼細節上的困惑。javascript

  項目架構及環境搭建很少敘述,github官網已經夠詳細了,直接開始擼源碼。java

    demo的使用上,和meepo大致一致,在須要分佈式事務的方法上,加上事務註解便可。git

1、@GlobalTransactional註解的使用

很普通的dubbo程序,再看下purchase方法github

  和meepo惟一不一樣的是,fescar使用了自定義註解@GlobalTransactional,meepo則是spring事務註解@Transactional。spring

  自定義註解表明了fescar棄用了JTA規範,本身承擔TM的角色,好處是加入分佈式事務後,不會影響到原來的事務實現。meepo默認會修改@Transactional標註的原單機事務實現,雖然不影響使用,但不算合理,能夠手動指定transmanager解決,如@Transactional("transactionManager1")。壞處則是放棄了spring 已經實現了的成熟穩定JTA規範。數組

  如今問題來了,一樣實現事務,一樣用的註解,二者實現機制同樣嗎?先說答案,是同樣的。本篇文章在講解@GlobalTransactional註解的同時,也過了一遍spring的@Transactional。緩存

  先跟蹤代碼,在第一張圖的的purchase處,打了一個斷點,debug到時,發現service是cglib生成的一個代理。第一個問題來了,爲何是cglib?spring的常識是,aop有接口的bean默認jdk動態代理,沒接口的cglib代理,或者xml配置 proxy-target-class="true" 強制使用cglib。可是在本例中,bussinesService有接口,xml未配置,卻爲什麼使用cglib代理?架構

  ok,這個問題一會再找找,咱們得先解決另一個重要的問題,代理類是何時生成的?框架

2、BeanPostProcessor接口

  這個問題好解答,就是BeanPostProcessor接口。fescar、dubbo不少框架包括spring自己,都有使用BeanPostProcessor來爲xml中配置的bean生成代理,我寫的meepo也有,這是spring很常見的一種擴展使用方式。分佈式

  看一下spirng的生命週期。 

 

  在xml裏配置的每個bean,在初始化的先後,會先找到緩存的BeanPostProcessor list,自己做爲參數,傳入BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法,爲bean作一些處理如生成代理類,而後返回。

  fercas的GlobalTransactionScanner承擔了這個角色,GlobalTransactionScanner繼承了spring aop包下的AbstractAutoProxyCreator類,這個類實現了InstantiationBeanPostProcessor接口,是spring實現aop最關鍵的一個抽象類,相似JTA事務裏的AbstractPlatformTransactionManager。

3、spring aop的核心類AbstractAutoProxyCreator

  默認狀況下,代理是經過AbstractAutoProxyCreator中的postProcessAfterInitialization()建立的。

上述方法的執行邏輯爲:
a.獲取緩存的bean名字,getCacheKey()方法其實是便於子類擴展。默認返回className_beanName
b.若earlyProxyReferences緩存中不包含當前cacheKey,則調用wrapIfNecessary()建立代理對象。
earlyProxyReferences這個緩存是幹嗎的呢?
從上下文中能夠找到getEarlyBeanReference()方法中會將相應的cacheKey保存到earlyProxyReferences中,同時也會調用wrapIfNecessary()方法,所以能夠看出該緩存用於保存已經建立過代理對象的cachekey,避免重複建立。代碼以下:

  那麼getEarlyBeanReference()方法又是什麼時候被調用的呢?
  經過分析能夠看到,getEarlyBeanReference()方法在SmartInstantiantionAwareBeanPostProcessor中聲明,咱們知道,spring爲了解決單例bean之間的循環依賴問題,提早將代理對象暴露出去。
  接下來是wrapIfNecessary方法,該方法是真正生成代理類的地方。GlobalTransactionScanner重寫了該方法,咱們來看一下。

  主要邏輯有:

  a. 查看當前bean是否有@GlobalTransactional註解,若是沒有,本身返回bean,再也不繼續生成代理。

  b. new了 一個實現了MethodInterceptor接口的方法攔截器GlobalTransactionalInterceptor,相似jdk動態代理的InvocationHandler,關鍵邏輯所在

  !AopUtils.isAopProxy(bean) 判斷是否已經是代理類,避免重複代理,若未被代理,則調用父類的wrapIfNecessary方法。

  AbstractAutoProxyCreator是全部使用spring aop的關鍵類,和GlobalTransactionScanner同樣,spring自身的一些aop機制也使用了AbstractAutoProxyCreator。咱們看下@Transactional的使用,配置xml後在須要事務的方法加上註解便可,以下

<!-- 事務管理器配置,單數據源事務 -->
<bean id="pkgouTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pkGouDataSource" />
</bean>
<!-- 使用annotation定義事務 -->
<tx:annotation-driven transaction-manager="pkgouTransactionManager" />

  tx標籤初始化的時候,由對應的解析器AnnotationDrivenBeanDefinitionParser來解析,在解析方法parse中,會註冊一個InfrastructureAdvisorAutoProxyCreator類,這個類繼承了AbstractAutoProxyCreator。使用普通的aop標籤,解析時也會註冊一個繼承AbstractAutoProxyCreator的AspectJAwareAdvisorAutoProxyCreator類。類結構圖以下:

 

  

AbstractAutoProxyCreator這個抽象類,有一個惟一抽象方法getAdvicesAndAdvisorsForBean。該方法做用是爲bean獲取全部適用的Advice和Advisor。

4、AOP基礎概念

Advice和Advisor以及PointCut都是aop都基礎概念,簡單理解下:

Advice 即通知,具體的處理邏輯,好比方法先後加日誌,具體實現能夠是MethodInterceptor的invoke方法;

PointCut 是要攔截的範圍,好比哪一個包下的controller;

Advisor是對Advice和PointCut的一個封裝,一個服務可能初始化了幾個Advisor,那麼bean初始化時,遍歷這些Advisor,先判斷本身是否在Advisor的PointCut範圍內,若是是,則生成aop代理。

  AbstractAutoProxyCreator的類結構圖能夠看到,它有一個子類AbstractAdvisorAutoProxyCreator,同時是幾個標籤解析生成的Creator的父類,AbstractAdvisorAutoProxyCreator的做用是自動獲取Advisor集合,咱們來看下它的getAdvicesAndAdvisorsForBean方法。

  findEligibleAdvisors處理兩件事

  • 找到Spring中Advisor的實現類(findCandidateAdvisors)
  • 將全部擁有@Aspect註解的類轉換爲advisors(aspectJAdvisorsBuilder.buildAspectJAdvisors)
如今問題來了,Advisor的實現類哪裏來?咱們看前面的標籤解析類AnnotationDrivenBeanDefinitionParser的configureAutoProxyCreator方法的一個片斷。

標籤解析的中間,動態生成一個Advisor

  • 定義一個Advisor
  • 注入interceptor即Advice
  • 註冊到spring容器
  以上幾步後, findEligibleAdvisors方法就能夠找到這個Advisor了。可能有人會有疑問,爲何上面沒有PointCut?這個後面會處理,沒有PointCut時會生成一個TruePointCut,表示所有範圍。畢竟@Transactional這種註解,並無限制使用的範圍。

5、fescar的 GlobalTransactionScanner如何處理

   GlobalTransactionScanner並無繼承AbstractAdvisorAutoProxyCreator,由於fercas的註解並無在xml裏配置,也不須要自動找其餘Advisor,它只要處理本身的攔截器就行了,看一下它的getAdvicesAndAdvisorsForBean方法。

  很簡單,就是返回了以前在wrap裏new的GlobalTransactionalInterceptor,GlobalTransactionalInterceptor實現MethodInterceptor,間接實現了Advice。繼續跟蹤wrap方法,獲取Advice和Advisor後,返回Object[](這裏是只有一個Advice)。接着把Object數組和bean,傳入到createProxy方法,生成代理類。

這裏關鍵是兩步:

  • Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); 
  • proxyFactory.getProxy(getProxyClassLoader());

buildAdvisors把Advice和Advisor的Object[] 混合物,處理後統一成Advisor[],放入new的一個ProxyFactory中,proxyFactory繼續生成代理類。

咱們看下buildAdvisors關鍵代碼

  若是Object[]裏的元素是Advisor,直接返回,若是是Advice並且是MethodInterceptor,包裝成一個DefaultPointcutAdvisor,這個Advisor的構造方法,生成一個默認的TruePointcut,也就是前面提到的所有範圍。

繼續看proxyFactory.getProxy(getProxyClassLoader()) 這句,終於到了選擇代理的地方,咱們看下。

  這裏傳入的config的屬性拷貝自GlobalTransactionScanner的屬性(點進方法能夠看到,不貼代碼了),當optimize或者ProxyTargetClass任一屬性是true的時候,會進入方法體,進一步判斷當前bean是不是接口,注意,這裏不是判斷是否實現接口,顯然,業務service是一個class,這時候,就會使用cglib來生成代理類。咱們來看下GlobalTransactionScanner構造方法。

  終於能夠解答了前面的問題,爲何是cglib的代理類。和xml配置效果同樣,只要找到這個屬性配置的地方就行了,若是沒配,默認是false。

  最後,動態代理的生成過程再也不敘述,代理類(jdk動態代理)執行業務接口方法的路徑是

$proxy.purchase -> InvocationHandler.invoke ->intercept.invoke 

  cglib代理類的路徑沒看,應該大致一致。

總結

  本篇主要講解了@GlobalTransactional 生成代理類的整個過程,其實和fercas框架關係不大,由於其原理和spring的@transactionanl註解基本同樣(@transactional詳解

  整理下咱們的思路,方法上的自定義註解,會爲方法所在的類生成aop代理,方法先後加上一些自定義邏輯,好比fescar的全局事務管理。生成代理的路徑以下:

  GlobalTransactionScanner.postProcessAfterInitialization->GlobalTransactionScanner.wrapIfNecessary->AbstractAutoProxyCreator.wrapIfNecessary->GlobalTransactionScanner.getAdvicesAndAdvisorsForBean->AbstractAutoProxyCreator.createProxy->AbstractAutoProxyCreator.buildAdvisors->proxyFactory.getProxy

  再總結一下,若是咱們想使用自定義註解生成AOP代理,須要哪些步驟:

  • 自定義註解
  • 編寫Intercept類
  • 編寫代理生成類繼承 AbstractAutoProxyCreator,xml中配置該bean
  • 重寫getAdvicesAndAdvisorsForBean 定義遍歷範圍,效果同xml中aop配置的PointCut
  • 重寫wrapIfNecessary ,過濾非本身定義註解的bean
相關文章
相關標籤/搜索