轉自:http://www.linkedkeeper.com/detail/blog.action?bid=1045html
Spring事務是咱們平常工做中常用的一項技術,Spring提供了編程、註解、aop切面三種方式供咱們使用Spring事務,其中編程式事務由於對代碼入侵較大因此不被推薦使用,註解和aop切面的方式能夠基於需求自行選擇,咱們以註解的方式爲例來分析Spring事務的原理和源碼實現。spring
首先咱們簡單看一下Spring事務的使用方式,配置:數據庫
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
複製代碼
在須要開啓事務的方法上加上@Transactional註解便可,這裏須要注意的是,當
<tx:annotation-driven transaction-manager="transactionManager1"/>
<bean id="transactionManager1"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager2"/>
<bean id="transactionManager2"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource2"/>
</bean>
複製代碼
這時第一個
下面咱們開始分析Spring的相關源碼,首先看一下對
(右鍵可查看大圖)this
這個方法比較長,關鍵的部分作了標記,最外圍的if判斷限制了
還記得當
這兩個方法的主要目的是註冊InfrastructureAdvisorAutoProxyCreator,註冊這個類的目的是什麼呢?咱們看下這個類的層次:3d
咱們發現這個類間接實現了BeanPostProcessor接口,咱們知道,Spring會保證全部bean在實例化的時候都會調用其postProcessAfterInitialization方法,咱們可使用這個方法包裝和改變bean,而真正實現這個方法是在其父類AbstractAutoProxyCreator類中:
上面這個方法相信你們已經看出了它的目的,先找出全部對應Advisor的類的beanName,再經過beanFactory.getBean方法獲取這些bean並返回。不知道你們仍是否記得在文章開始的時候提到的三個類,其中BeanFactoryTransactionAttributeSourceAdvisor實現了Advisor接口,因此這個bean就會在此被提取出來,而另外兩個bean被織入了BeanFactoryTransactionAttributeSourceAdvisor當中,因此也會一塊兒被提取出來,下圖爲BeanFactoryTransactionAttributeSourceAdvisor類的層次:
下面讓咱們來看Spring如何在全部候選的加強器中獲取匹配的加強器:
上面的方法中提到引介加強的概念,在此作簡要說明,引介加強是一種比較特殊的加強類型,它不是在目標方法周圍織入加強,而是爲目標類建立新的方法和屬性,因此引介加強的鏈接點是類級別的,而非方法級別的。經過引介加強,咱們能夠爲目標類添加一個接口的實現,即原來目標類未實現某個接口,經過引介加強能夠爲目標類建立實現該接口的代理,使用方法能夠參考文末的引用連接。另外這個方法用兩個重載的canApply方法爲目標類尋找匹配的加強器,其中第一個canApply方法會調用第二個canApply方法並將第三個參數傳爲false:
在上面BeanFactoryTransactionAttributeSourceAdvisor類的層次中咱們看到它實現了PointcutAdvisor接口,因此會調用紅框中的canApply方法進行判斷,第一個參數pca.getPointcut()也就是調用BeanFactoryTransactionAttributeSourceAdvisor的getPointcut方法:
這裏的transactionAttributeSource也就是咱們在文章開始看到的爲BeanFactoryTransactionAttributeSourceAdvisor織入的兩個bean中的AnnotationTransactionAttributeSource,咱們以TransactionAttributeSourcePointcut做爲第一個參數繼續跟蹤canApply方法:
咱們跟蹤pc.getMethodMatcher()方法也就是TransactionAttributeSourcePointcut的getMethodMatcher方法是在它的父類中實現:
發現方法直接返回this,也就是下面methodMatcher.matches方法就是調用TransactionAttributeSourcePointcut的matches方法:
在上面咱們看到其實這個tas就是AnnotationTransactionAttributeSource,這裏的目的其實也就是判斷咱們的業務方法或者類上是否有@Transactional註解,跟蹤AnnotationTransactionAttributeSource的getTransactionAttribute方法:
方法中的事務聲明優先級最高,若是方法上沒有聲明則在類上尋找:
this.annotationParsers是在AnnotationTransactionAttributeSource類初始化的時候初始化的:
因此annotationParser.parseTransactionAnnotation就是調用SpringTransactionAnnotationParser的parseTransactionAnnotation方法:
至此,咱們終於看到的Transactional註解,下面無疑就是解析註解當中聲明的屬性了:
在這個方法中咱們看到了在Transactional註解中聲明的各類經常使用或者不經常使用的屬性的解析,至此,事務的初始化工做算是完成了,下面開始真正的進入執行階段。
在上文AbstractAutoProxyCreator類的wrapIfNecessary方法中,獲取到目標bean匹配的加強器以後,會爲bean建立代理,這部份內容咱們會在Spring AOP的文章中進行詳細說明,在此簡要說明方便你們理解,在執行代理類的目標方法時,會調用Advisor的getAdvice獲取MethodInterceptor並執行其invoke方法,而咱們本文的主角BeanFactoryTransactionAttributeSourceAdvisor的getAdvice方法會返回咱們在文章開始看到的爲其織入的另一個bean,也就是TransactionInterceptor,它實現了MethodInterceptor,因此咱們分析其invoke方法:
這個方法很長,可是總體邏輯仍是很是清晰的,首選獲取事務屬性,這裏的getTransactionAttrubuteSource()方法的返回值一樣是在文章開始咱們看到的被織入到TransactionInterceptor中的AnnotationTransactionAttributeSource,在事務準備階段已經解析過事務屬性並保存到緩存中,因此這裏會直接從緩存中獲取,接下來獲取配置的TransactionManager,也就是determineTransactionManager方法,這裏若是配置沒有指定transaction-manager而且也沒有默認id名爲transactionManager的bean,就會報錯,而後是針對聲明式事務和編程式事務的不一樣處理,建立事務信息,執行目標方法,最後根據執行結果進行回滾或提交操做,咱們先分析建立事務的過程。在分析以前但願你們能先去了解一下Spring的事務傳播行爲,有助於理解下面的源碼,這裏作一個簡要的介紹,更詳細的信息請你們自行查閱Spring官方文檔,裏面有更新詳細的介紹。
Spring的事務傳播行爲定義在Propagation這個枚舉類中,一共有七種,分別爲:
REQUIRED:業務方法須要在一個容器裏運行。若是方法運行時,已經處在一個事務中,那麼加入到這個事務,不然本身新建一個新的事務,是默認的事務傳播行爲。
NOT_SUPPORTED:聲明方法不須要事務。若是方法沒有關聯到一個事務,容器不會爲他開啓事務,若是方法在一個事務中被調用,該事務會被掛起,調用結束後,原先的事務會恢復執行。
REQUIRESNEW:不論是否存在事務,該方法總彙爲本身發起一個新的事務。若是方法已經運行在一個事務中,則原有事務掛起,新的事務被建立。
MANDATORY:該方法只能在一個已經存在的事務中執行,業務方法不能發起本身的事務。若是在沒有事務的環境下被調用,容器拋出例外。
SUPPORTS:該方法在某個事務範圍內被調用,則方法成爲該事務的一部分。若是方法在該事務範圍外被調用,該方法就在沒有事務的環境下執行。
NEVER:該方法絕對不能在事務範圍內執行。若是在就拋例外。只有該方法沒有關聯到任何事務,才正常執行。
NESTED:若是一個活動的事務存在,則運行在一個嵌套的事務中。若是沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個能夠回滾的保存點。內部事務的回滾不會對外部事務形成影響。它只對DataSourceTransactionManager事務管理器起效。
判斷當前線程是否存在事務就是判斷記錄的數據庫鏈接是否爲空而且transactionActive狀態爲true。
REQUIRESNEW會開啓一個新事務並掛起原事務,固然開啓一個新事務就須要一個新的數據庫鏈接:
suspend掛起操做主要目的是將當前connectionHolder置爲null,保存原有事務信息,以便於後續恢復原有事務,並將當前正在進行的事務信息進行重置。下面咱們看Spring如何開啓一個新事務:
這裏咱們看到了數據庫鏈接的獲取,若是是新事務須要獲取新一個新的數據庫鏈接,併爲其設置了隔離級別、是否只讀等屬性,下面就是將事務信息記錄到當前線程中:
接下來就是記錄事務狀態並返回事務信息:
而後就是咱們目標業務方法的執行了,根據執行結果的不一樣作提交或回滾操做,咱們先看一下回滾操做:
其中回滾條件默認爲RuntimeException或Error,咱們也能夠自行配置。
保存點通常用於嵌入式事務,內嵌事務的回滾不會引發外部事務的回滾。下面咱們來看新事務的回滾:
很簡單,就是獲取當前線程的數據庫鏈接並調用其rollback方法進行回滾,使用的是底層數據庫鏈接提供的API。最後還有一個清理和恢復掛起事務的操做:
若是事務執行前有事務掛起,那麼當前事務執行結束後須要將掛起的事務恢復,掛起事務時保存了原事務信息,重置了當前事務信息,因此恢復操做就是將當前的事務信息設置爲以前保存的原事務信息。到這裏事務的回滾操做就結束了,下面讓咱們來看事務的提交操做:
在上文分析回滾流程中咱們提到了若是當前事務不是獨立的事務,也沒有保存點,在回滾的時候只是設置一個回滾標記,由外部事務提交時統一進行總體事務的回滾。
提交操做也是很簡單的調用數據庫鏈接底層API的commit方法。
參考連接:
http://blog.163.com/asd_wll/blog/static/2103104020124801348674/
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#spring-data-tier