文章有點(diǎn)長,我決定用半個(gè)小時(shí)來和你分享~???? 廢話不多說,上代碼。。。
創(chuàng)新互聯(lián)公司專注于什邡企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城開發(fā)。什邡網(wǎng)站建設(shè)公司,為什邡等地區(qū)提供建站服務(wù)。全流程定制開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
基于Seata 1.5.2,項(xiàng)目中用 seata-spring-boot-starter
1. SeataDataSourceAutoConfiguration
SeataDataSourceAutoConfiguration 主要是配置數(shù)據(jù)源代理,可以看到:
先記一下,SeataAutoDataSourceProxyCreator是一個(gè)BeanPostProcessor
剛才new了一個(gè)SeataAutoDataSourceProxyCreator,繼續(xù)看構(gòu)造方法,默認(rèn)useJdkProxy是false,excludes為空,dataSourceProxyMode是AT
構(gòu)造方法中最重要的一件事情是構(gòu)造AOP通知(攔截器),這里new了一個(gè)SeataAutoDataSourceProxyAdvice
SeataAutoDataSourceProxyAdvice是一個(gè)MethodInterceptor。
MethodInterceptor是aop中的一個(gè)接口,當(dāng)目標(biāo)方法被調(diào)用時(shí)就會調(diào)用與之關(guān)聯(lián)的MethodInterceptor的invoke方法
至此,在構(gòu)造方法中完成了advisors的賦值,advisors[]中有一個(gè)DefaultIntroductionAdvisor,DefaultIntroductionAdvisor中引用了SeataAutoDataSourceProxyAdvice
前面說過,SeataAutoDataSourceProxyCreator是一個(gè)BeanPostProcessor,而BeanPostProcessor是BeanFactory中的一個(gè)鉤子(回調(diào)),稱之為后置處理器
AbstractAutoProxyCreator#postProcessBeforeInstantiation()
AbstractAutoProxyCreator#postProcessAfterInitialization()
AbstractAutoProxyCreator#wrapIfNecessary()
AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean()
SeataAutoDataSourceProxyCreator#getAdvicesAndAdvisorsForBean()
SeataAutoDataSourceProxyCreator#wrapIfNecessary()
DataSourceProxyHolder維護(hù)了數(shù)據(jù)源對象與數(shù)據(jù)源代理對象的映射
至此,數(shù)據(jù)源代理部分就看完了,下面總結(jié)一下:
1、啟動(dòng)的時(shí)候自動(dòng)配置數(shù)據(jù)源代理,創(chuàng)建了一個(gè)SeataAutoDataSourceProxyCreator
2、SeataAutoDataSourceProxyCreator在構(gòu)造方法中創(chuàng)建AOP通知,并賦值給其屬性
3、AbstractAutoProxyCreator是一個(gè)抽象類不能被實(shí)例化,能實(shí)例化的只有SeataAutoDataSourceProxyCreator
4、SeataAutoDataSourceProxyCreator從AbstractAutoProxyCreator那里繼承了很多屬性和方法,其中就包括postProcessBeforeInstantiation()、postProcessAfterInitialization()、createProxy()等等
5、SeataAutoDataSourceProxyCreator間接實(shí)現(xiàn)了BeanPostProcessor接口,也就是說它也是BeanPostProcessor的一個(gè)實(shí)現(xiàn)類
6、BeanFactory回調(diào)所有的BeanPostProcessor#postProcessAfterInitialization()時(shí),就會調(diào)用SeataAutoDataSourceProxyCreator的postProcessAfterInitialization()方法,最終會調(diào)用wrapIfNecessary()方法
7、wrapIfNecessary()只關(guān)心DataSource對象,它負(fù)責(zé)為DataSource對象生成代理對象,并且在SeataAutoDataSourceProxyCreator中維護(hù)了DataSource對象與SeataDataSourceProxy對象之間的映射關(guān)心
8、創(chuàng)建代理對象時(shí),會給DataSource對象應(yīng)用AOP攔截器。用AOP的話來講,就是給目標(biāo)對象DataSource織入通知,并創(chuàng)建一個(gè)被增強(qiáng)的代理對象
9、通知(攔截器)是SeataAutoDataSourceProxyAdvice,它實(shí)現(xiàn)了MethodInterceptor接口
10、SeataAutoDataSourceProxyAdvice#invoke()方法所做的事情就是,拿到原始DataSource的代理對象,并且在代理對象上調(diào)用目標(biāo)方法
綜上所述,以上做的所有工作都是為了將來調(diào)用 javax.sql.DataSource 上的任意方法時(shí)都會被攔截,然后調(diào)用其代理對象上對應(yīng)的方法。而DataSource中最重要的一個(gè)方法就是getConnection()
劃重點(diǎn):將來,所有調(diào)用 javax.sql.DataSource#getConnection() 都會被攔截,然后在代理對象上執(zhí)行g(shù)etConnection(),因此可以這樣說
調(diào) javax.sql.DataSource#getConnection() 實(shí)際上執(zhí)行的是 io.seata.rm.datasource.SeataDataSourceProxy#getConnection()
2. SeataAutoConfiguration
SeataAutoConfiguration里面主要是配置GlobalTransactionScanner(全局事務(wù)掃描器)
seata.enabled=true 才會開啟 SeataAutoConfiguration
GlobalTransactionScanner 也繼承自 AbstractAutoProxyCreator,同時(shí)還實(shí)現(xiàn)了InitializingBean接口。BeanFactory在設(shè)置了所有bean屬性之后會調(diào)用InitializingBean的afterPropertiesSet()方法
GlobalTransactionScanner#afterPropertiesSet()
io.seata.common.DefaultValues中定義了很多默認(rèn)值
同樣地,因?yàn)閷?shí)現(xiàn)了BeanPostProcessor接口,所以在啟動(dòng)時(shí)BeanFactory實(shí)例化Bean之后,會調(diào)用GlobalTransactionScanner的postProcessAfterInitialization(),盡管這個(gè)postProcessAfterInitialization()方法時(shí)從AbstractAutoProxyCreator那里繼承來的,但是不影響啊,還是會調(diào)用GlobalTransactionScanner這個(gè)bean的postProcessAfterInitialization()方法。于是,最終又會調(diào)wrapIfNecessary()方法。
GlobalTransactionScanner#wrapIfNecessary()
這里面有一個(gè)很重要的邏輯就是,創(chuàng)建了一個(gè)GlobalTransactionalInterceptor對象,并賦值給interceptor
AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean()是一個(gè)抽象方法,實(shí)現(xiàn)在子類GlobalTransactionScanner中
因此,所有在GlobalTransactionScanner#wrapIfNecessary()中被代理的對象,都被應(yīng)用GlobalTransactionalInterceptor
GlobalTransactionalInterceptor也是一個(gè)MethodInterceptor
也就是說,目標(biāo)方法的調(diào)用都會轉(zhuǎn)到GlobalTransactionalInterceptor#invoke()上
GlobalTransactionalInterceptor#handleGlobalTransaction()
事務(wù)執(zhí)行直接調(diào)用TransactionalTemplate的execute()方法
io.seata.tm.api.TransactionalTemplate#execute()
io.seata.tm.api.GlobalTransactionContext#getCurrent() 獲取當(dāng)前事務(wù)
io.seata.tm.api.TransactionalTemplate#beginTransaction()
tx是DefaultGlobalTransaction
io.seata.tm.api.DefaultGlobalTransaction#begin()
DefaultGlobalTransaction中的TransactionManager是DefaultTransactionManager
DefaultTransactionManager中提供了事務(wù)相關(guān)的底層操作
io.seata.tm.api.DefaultGlobalTransaction#commit()
io.seata.tm.api.DefaultGlobalTransaction#rollback()的邏輯與commit()類似,都是重試調(diào)用transactionManager.rollback(xid)
全局事務(wù)掃描器部分的代碼就看到這里,下面總結(jié)一下:
1、配置項(xiàng)seata.enabled=true 會觸發(fā) SeataAutoConfiguration 自動(dòng)配置
2、SeataAutoConfiguration中創(chuàng)建了一個(gè)GlobalTransactionScanner
3、GlobalTransactionScanner繼承了AbstractAutoProxyCreator,并實(shí)現(xiàn)InitializingBean接口
4、初始化TM、RM
5、由于繼承了AbstractAutoProxyCreator,所以BeanFactory會調(diào)用GlobalTransactionScanner#方法postProcessAfterInitialization(),最終會調(diào)用GlobalTransactionScanner#wrapIfNecessary()來為目標(biāo)對象創(chuàng)建代理對象
6、GlobalTransactionScanner#wrapIfNecessary()中創(chuàng)建了一個(gè)GlobalTransactionalInterceptor,GlobalTransactionalInterceptor是一個(gè)MethodInterceptor
7、在創(chuàng)建代理對象的時(shí)候,在AbstractAutoProxyCreator#wrapIfNecessary()方法中,為代理對象應(yīng)用GlobalTransactionalInterceptor,于是所有目標(biāo)對象上的方法調(diào)用就會轉(zhuǎn)為調(diào)用GlobalTransactionalInterceptor#invoke()
8、GlobalTransactionalInterceptor#invoke()方法中,首先獲取被調(diào)用的目標(biāo)對象的Class和Method對象,然后檢查目標(biāo)方法或類上是否有@GlobalTransactional或@GlobalLock注解,而且配置項(xiàng)中不能禁用全局事務(wù)
9、如果加了@GlobalTransactional注解,則創(chuàng)建一個(gè)AspectTransactional,然后開始處理全局事務(wù),默認(rèn)傳播特性是REQUIRED
10、如果加了@GlobalLock注解,則開始處理全局鎖
11、處理全局事務(wù)就是直接調(diào)用事務(wù)模板中的execute方法,TransactionalTemplate#execute()是一個(gè)模板方法,其中定義了事務(wù)處理的流程。首先開啟事務(wù),然后執(zhí)行業(yè)務(wù)邏輯,最后提交事務(wù),異?;貪L事務(wù)。
12、事務(wù)操作是在DefaultGlobalTransaction中處理的,最終處理在DefaultTransactionManager。DefaultTransactionManager負(fù)責(zé)同步遠(yuǎn)程調(diào)用,向TC發(fā)請求來開啟、提交、回滾事務(wù)等操作
3. 數(shù)據(jù)庫操作執(zhí)行SQL語句
通過Java自帶的JDBC操作數(shù)據(jù)庫通常是這樣的:
Class.forName(driverClass);
// 獲取Connection
Connection connection = DriverManager.getConnection(url,user,password);
// 創(chuàng)建Statement或者PreparedStatement
Statement stmt = connection.createStatement();
stmt.execute(sql);
// PreparedStatement ps = connection.prepareStatement(sql);
// ps.execute();
MyBatis底層也是這一套
接下來看Seata是如何做的
首先是獲取數(shù)據(jù)庫連接Connection,前面已經(jīng)說過了,調(diào)用DataSource的getConnection()方法底層是在代理對象SeataDataSourceProxy上調(diào)用getConnection()。SeataDataSourceProxy是接口,如果是AT模式,則這個(gè)數(shù)據(jù)源代理對象是DataSourceProxy
DataSourceProxy#getConnection()獲取數(shù)據(jù)庫連接
ConnectionProxy#createStatement()
ConnectionProxy#prepareStatement()
PreparedStatementProxy 繼承自 StatementProxy,因此下面就直接看PreparedStatementProxy如何執(zhí)行SQL
PreparedStatementProxy#execute()
ExecuteTemplate#execute() 是一個(gè)模板方法
挑一個(gè)看看吧,就挑UpdateExecutor
UpdateExecutor構(gòu)造方法中一直調(diào)父類的構(gòu)造法,既然如此,那么直接看BaseTransactionalExecutor
UpdateExecutor#execute()
這個(gè)方法時(shí)從BaseTransactionalExecutor那里繼承來的,又是一個(gè)模板方法,可見設(shè)計(jì)模式是多么重要
AbstractDMLBaseExecutor#doExecute()
AbstractDMLBaseExecutor#executeAutoCommitTrue()
ConnectionProxy#changeAutoCommit()
現(xiàn)在事務(wù)自動(dòng)提交已經(jīng)被Seata改成false了
UpdateExecutor#beforeImage()
BaseTransactionalExecutor#prepareUndoLog()
接下來,提交事務(wù)
ConnectionProxy#commit()
ConnectionProxy#processGlobalTransactionCommit() 處理全局事務(wù)提交
分支事務(wù)提交以后,業(yè)務(wù)數(shù)據(jù)更改和undo_log就都提交了
回想一下,為什么在執(zhí)行業(yè)務(wù)修改前要先將默認(rèn)的自動(dòng)提交改成手動(dòng)提交,最后再改成自動(dòng)提交呢?
因?yàn)?,要將業(yè)務(wù)數(shù)據(jù)修改和插入undo_log放在同一個(gè)事務(wù)里,一起提交
這一切都?xì)w功于代理
回顧一下整個(gè)調(diào)用鏈
結(jié)合之前的案例,AT模式TC、TM、RM三者的交互應(yīng)該是這樣的:
問題一:為什么在執(zhí)行的時(shí)候,先將數(shù)據(jù)庫自動(dòng)提交autoCommit設(shè)為false,最后再改成true呢?
答:因?yàn)?,需要將undo_log和業(yè)務(wù)數(shù)據(jù)修改放到同一個(gè)事務(wù)中,這樣可以保證業(yè)務(wù)數(shù)據(jù)修改成功后undo_log必然插入成功,所以Seata要將其改為手動(dòng)提交。最后再改成true是因?yàn)槟J(rèn)autoCommit就是true,這樣可以不影響其它業(yè)務(wù)。
問題二:什么情況下ConnectionContext中xid=null,且isGlobalLockRequire=true呢?或者換一種問法,什么情況下不在全局事務(wù)中,當(dāng)仍然需要全局鎖呢?
答:當(dāng)業(yè)務(wù)方法上不加@GlobalTransactional,而是只加了@GlobalLock注解的情況下,就會出現(xiàn)上述情況,也就會執(zhí)行 ConnectionProxy#processLocalCommitWithGlobalLocks()方法,在事務(wù)提交前檢查全局鎖,這樣設(shè)計(jì)的目的是在AT模式下,不出現(xiàn)臟讀、臟寫。由于數(shù)據(jù)源被代理了,當(dāng)一個(gè)加了@GlobalTransactional的全局事務(wù),與另一個(gè)加了@GlobalTransactional或@GlobalLock注解的事務(wù)在本地事務(wù)提交前就會檢查全局鎖,要先獲得全局鎖才能提交本地事務(wù),這樣就避免了臟讀臟寫,從而相當(dāng)于實(shí)現(xiàn)了全局事務(wù)的讀已提交隔離級別。參見:https://seata.io/zh-cn/blog/seata-at-lock.html
關(guān)于Seata 1.5.2 Client端的源碼學(xué)習(xí)就先到這里,歡迎交流~
如果你都已經(jīng)看到了這里,不妨給我點(diǎn)個(gè)贊吧????
本文名稱:Seata 1.5.2 源碼學(xué)習(xí)
網(wǎng)頁地址:http://chinadenli.net/article44/dsoioee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、網(wǎng)站設(shè)計(jì)公司、域名注冊、手機(jī)網(wǎng)站建設(shè)、小程序開發(fā)、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)