前言
今天是平安夜,先祝大家平安夜快樂撩荣。
我們之前的數(shù)十篇文章分析了 Spring 和 Mybatis 的原理,基本上從源碼層面都了解了他們的基本原理抄罕,那么毫痕。在我們?nèi)粘J褂眠@些框架的時(shí)候征峦,還有哪些疑問呢?就樓主而言消请,樓主已經(jīng)明白了 IOC 栏笆,AOP 的原理,也明白了 Mybatis 的原理臊泰,也明白了 Spring 和 Mybatis 是如何整合的蛉加。但是,我們漏掉了 JavaEE 中一個(gè)非常重要的特性:事務(wù)因宇。事務(wù)是 Java 程序員開發(fā)程序時(shí)不可避免的問題七婴。我們就不討論 ACID 的事務(wù)特性祟偷,樓主這里假定大家都已經(jīng)了了解了事務(wù)的原理察滑。如果還不了解,可以先去谷歌看看修肠。那么贺辰,我們今天的任務(wù)是剖析源碼,看看Spring 是怎么運(yùn)行事務(wù)的嵌施,并且是基于當(dāng)前最流行的SpringBoot饲化。還有,我們之前剖析Mybatis 的時(shí)候吗伤,也知道吃靠,Mybatis 也有事務(wù),那么足淆,他倆融合之后巢块,事務(wù)是交給誰的礁阁?又是怎么切換的?今天這幾個(gè)問題族奢,我們都要從源碼中找到答案姥闭。
1. Spring 的事務(wù)如何運(yùn)行?
如果各位使用過SpringBoot 越走,那么就一定知道如何在Spring中使用注解棚品,比如在一個(gè)類或者一個(gè)方法上使用 @Transactional 注解,在一個(gè)配置類上加入一個(gè) @EnableTransactionManagement 注解代表啟動(dòng)事務(wù)廊敌。而這個(gè)配置類需要實(shí)現(xiàn) TransactionManagementConfigurer 事務(wù)管理器配置接口铜跑。并實(shí)現(xiàn) annotationDrivenTransactionManager 方法返回一個(gè)包含了 配置好數(shù)據(jù)源的 DataSourceTransactionManager 事務(wù)對(duì)象。這樣就完成了事務(wù)配置庭敦,就可以在Spring使用事務(wù)的回滾或者提交功能了疼进。
這個(gè) saveList 方法就在Spring事務(wù)的控制之下,如果發(fā)生了異常秧廉,就會(huì)回滾事務(wù)伞广。如果各位知道更多的Spring的事務(wù)特性,可以在注解中配置疼电,比如什么異常才能回滾嚼锄,比如超時(shí)時(shí)間,比如隔離級(jí)別蔽豺,比如事務(wù)的傳播区丑。就更有利于理解今天的文章了。
我們基于一個(gè) Junit 測(cè)試用例修陡,來看看Spring的事務(wù)時(shí)如何運(yùn)行的沧侥。
在測(cè)試用例中執(zhí)行該方法,參數(shù)時(shí)一個(gè)空的List魄鸦,這個(gè)Sql的運(yùn)行肯定是失敗的宴杀。我們主要看看他的運(yùn)行過程。我們講斷點(diǎn)打在該方法上拾因。斷點(diǎn)進(jìn)入該方法旺罢。
注意,dataCollectionShareService 對(duì)象已經(jīng)被 Cglib 代理了绢记,那么他肯定會(huì)走 DynamicAdvisedInterceptor 的 intercept 方法扁达,我們斷點(diǎn)進(jìn)入該方法查看,這個(gè)方法我們已經(jīng)很屬性了蠢熄,該方法中跪解,最重要的事情就是執(zhí)行通知器或者攔截器的方法,那么签孔,該代理有通知器嗎叉讥?
有一個(gè)通知器砾跃。是什么呢?
一個(gè)事務(wù)攔截器节吮,也就是說抽高,如果通知器鏈不為空,就會(huì)依次執(zhí)行通知器鏈的方法透绩。那么 TransactionInterceptor 到底是什么呢翘骂?
該類實(shí)現(xiàn)了通知器接口,也實(shí)現(xiàn)類 MethodInterceptor 接口帚豪,并實(shí)現(xiàn)了該接口的 invoke 方法碳竟,在 DynamicAdvisedInterceptor 的 intercept 方法中,最終會(huì)調(diào)用每個(gè) MethodInterceptor 的 invoke 方法狸臣,那么莹桅,TransactionInterceptor 的 invoke 方法是如何實(shí)現(xiàn)的呢?
invoke 方法中會(huì)調(diào)用自身的 invokeWithinTransaction 方法烛亦,看名字诈泼,該方法和事務(wù)相關(guān)。該方法參數(shù)是由目標(biāo)方法煤禽,目標(biāo)類铐达,一個(gè)回調(diào)對(duì)象構(gòu)成。 那么我們就進(jìn)入該方法查看檬果,該方法很長(zhǎng):
/**
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
該方法主要邏輯:
- 獲取事務(wù)屬性瓮孙,根據(jù)事務(wù)屬性,獲取事務(wù)管理器选脊。
- 判斷屬性是否空杭抠,或者事務(wù)管理器是否不是 CallbackPreferringPlatformTransactionManager 類型,如果是該類型恳啥,則會(huì)執(zhí)行事務(wù)管理器的 execute 方法偏灿。
- 生成一個(gè)封裝了事務(wù)管理器,事務(wù)屬性角寸,方法簽名字符串菩混,事務(wù)狀態(tài)對(duì)象 的 TransactionInfo 事務(wù)信息對(duì)象忿墅。該對(duì)象會(huì)在事務(wù)回滾或者失敗時(shí)起作用扁藕。
- 調(diào)用目標(biāo)對(duì)象方法或者是下一個(gè)過濾器的方法。
- 如果方法由異常則執(zhí)行 completeTransactionAfterThrowing 方法疚脐,調(diào)用事務(wù)管理器的回滾方法亿柑。如果沒有異常,調(diào)用 commitTransactionAfterReturning 提交方法棍弄。最后返回返回值望薄。
可以說疟游,該方法就是Spring 事務(wù)的核心調(diào)用,根據(jù)目標(biāo)方法是否有異常進(jìn)行事務(wù)的回滾痕支。
那么颁虐,我們需要一行一行的看看該方法實(shí)現(xiàn)。
首先看事務(wù)的屬性卧须。
2. TransactionAttribute 事務(wù)屬性
invokeWithinTransaction 方法中調(diào)用了 自身的 getTransactionAttributeSource 方法返回一個(gè)TransactionAttributeSource 對(duì)象另绩,并調(diào)用該對(duì)象的 getTransactionAttribute 方法,參數(shù)是目標(biāo)方法和目標(biāo)類對(duì)象花嘶。首先看 getTransactionAttributeSource 方法笋籽,該方法直接返回了抽象類 TransactionAspectSupport 中定義的 TransactionAttributeSource 屬性。該屬性的是什么時(shí)候生成的我們稍后再說椭员。我們debug 后返回的是 TransactionAttributeSource 接口的實(shí)現(xiàn)類 AnnotationTransactionAttributeSource 车海,看名字,注解事務(wù)屬性資源隘击,名字起的好很重要啊侍芝。我們進(jìn)入該類查看。
這是該類的繼承機(jī)構(gòu)圖埋同。我們重點(diǎn)還是關(guān)注該類的 getTransactionAttribute 方法竭贩,該方法有抽象類 AbstractFallbackTransactionAttributeSource 也就是 AnnotationTransactionAttributeSource 的父類完成。我們看看該方法莺禁。
該方法大部分都是緩存判斷留量,最重要的一行代碼樓主已紅框標(biāo)出。computeTransactionAttribute 方法哟冬,計(jì)算事務(wù)屬性楼熄。進(jìn)入該方法查看:
該方法是返回事務(wù)屬性的核心方法,首先浩峡,根據(jù) class 和 method 對(duì)象可岂,生成一個(gè)完整的method 對(duì)象,然后調(diào)用 findTransactionAttribute 方法翰灾,參數(shù)就是該 method 對(duì)象缕粹,findTransactionAttribute 方法是抽象方法,由子類實(shí)現(xiàn)纸淮,可見 computeTransactionAttribute 是個(gè)模板方法模式平斩。那么我們就看看他的子類 AnnotationTransactionAttributeSource 是如何實(shí)現(xiàn)的。該方法調(diào)用了自身的 determineTransactionAttribute 方法咽块。該方法實(shí)現(xiàn)入下:
該方法會(huì)判斷該 Method 對(duì)象是否含有注解绘面。并循環(huán) AnnotationTransactionAttributeSource 對(duì)象的 annotationParsers 注解解析器集合,對(duì)該方法進(jìn)行解析。如果解析成功揭璃,則返回該注解元素晚凿。我想我們也已經(jīng)猜到了,這個(gè)注解解析器解析的就是 @Transactional 注解瘦馍。
3. @Transactional 注解解析器 SpringTransactionAnnotationParser
我們說AnnotationTransactionAttributeSource 對(duì)象中又多個(gè)解析器歼秽。那么這些解析器是什么時(shí)候生成的呢?構(gòu)造方法中生成的情组。
該構(gòu)造方法由一個(gè)布爾屬性哲银,然后創(chuàng)建一個(gè)鏈表,也創(chuàng)建一個(gè) SpringTransactionAnnotationParser 對(duì)象添加進(jìn)鏈表中呻惕。這樣就完成了解析器的創(chuàng)建荆责。構(gòu)造方法什么時(shí)候調(diào)用的呢?我們稍后再講亚脆。
我們看看注解解析器是怎么解析方法對(duì)象的做院。
首先根據(jù)指定的 Transactional 注解和給定的方法,調(diào)用工具方法 getMergedAnnotationAttributes 濒持,獲取方法上的注解屬性键耕。然后調(diào)用重載方法 parseTransactionAnnotation 。
可以看到柑营,該方法首先創(chuàng)建了一個(gè) RuleBasedTransactionAttribute 對(duì)象屈雄,然后一個(gè)個(gè)解析注解中的元素,并將這些元素設(shè)置到 RuleBasedTransactionAttribute 對(duì)象中官套,注意酒奶,其中有個(gè) RollbackRuleAttribute 的集合,存儲(chǔ)著該注解屬性的回滾相關(guān)的屬性奶赔。最后添加到 RuleBasedTransactionAttribute 的RollbackRules 集合中惋嚎。
到這里,就完成了解析器的解析站刑。返回了一個(gè) RuleBasedTransactionAttribute 對(duì)象另伍。
回到 攔截器的 invokeWithinTransaction 方法中,此時(shí)已經(jīng)獲取了 屬性對(duì)象绞旅。根據(jù)方法摆尝,也就是說,如果返回值是null因悲,說明該方法沒有事務(wù)注解堕汞,在 getTransactionAttribute 方法中,也會(huì)將該方法作為key 囤捻,NULL_TRANSACTION_ATTRIBUTE 作為 value臼朗,放入緩存,如果不為null蝎土,那么就將 TransactionAttribute 作為 value 放入緩存视哑。
有了事務(wù)屬性,再獲取事務(wù)管理器誊涯。也就是 determineTransactionManager 方法挡毅。
4. 事務(wù)管理器。
我們注意到暴构,調(diào)用了自身的 determineTransactionManager 方法跪呈,返回了一個(gè) PlatformTransactionManager 事務(wù)管理器。這個(gè)事務(wù)管理器就是我們?cè)谖覀兊呐渲妙愔袑懙模?/p>
那么這個(gè)事務(wù)管理器是什么呢取逾?事務(wù)管理器就是真正執(zhí)行事務(wù)回滾或提交的執(zhí)行單位耗绿,我們看看該類:
繼承圖:
結(jié)構(gòu)圖:
紅框標(biāo)注的方法就是執(zhí)行正在事務(wù)邏輯的方法,其中又封裝了數(shù)據(jù)源砾隅,也就是 JDBC 的 Connection 误阻。比如 doCommit 方法:
我們看看determineTransactionManager 是如何獲取事務(wù)管理器的。
該方法步驟入下:
- 如果事務(wù)屬性為null 或者 容器工廠為null晴埂,則返會(huì)自身的 transactionManager 事務(wù)管理器究反。
- 如果都不為null,則獲取事務(wù)屬性的限定符號(hào)儒洛,根據(jù)限定符從容器中獲取 事務(wù)管理器精耐。
- 如果沒有限定符,則根據(jù)事務(wù)管理器的BeanName從容器中獲取琅锻。
- 如果都沒有卦停,則獲取自身的事務(wù)管理器,如果自身還沒有恼蓬,則從緩存中取出默認(rèn)的沫浆。如果默認(rèn)的還沒有,則從容器中獲取PlatformTransactionManager 類型的事務(wù)管理器滚秩,最后返回专执。
這里重點(diǎn)是自身的事務(wù)管理器從何而來?我們先按下不表郁油。
到這里本股,我們已經(jīng)有了事務(wù)管理器。就需要執(zhí)行 invokeWithinTransaction 下面的邏輯了桐腌≈粝裕回到 invokeWithinTransaction 方法,我們的返回值肯定滿足第一個(gè)if 條件案站,因?yàn)槲覀兊氖聞?wù)管理器不是 CallbackPreferringPlatformTransactionManager 類型的躬审。進(jìn)入if 塊。
首先創(chuàng)建一個(gè)事務(wù)信息對(duì)象。該類是什么呢承边?
屬性:
構(gòu)造方法:
該類包含了一個(gè) 事務(wù)管理器遭殉,事務(wù)屬性,事務(wù)方法字符串博助。
接著執(zhí)行回調(diào)類InvocationCallback 的 proceedWithInvocation 方法险污,該方法會(huì)執(zhí)行下一個(gè)通知器的攔截方法(如果有的話),最后執(zhí)行目標(biāo)方法富岳,這里蛔糯,目標(biāo)方法被 try 住了,如果發(fā)生異常窖式,則執(zhí)行completeTransactionAfterThrowing 方法蚁飒,并拋出異常,在 finally 塊中執(zhí)行清理工作萝喘。如果成功執(zhí)行飒箭,則執(zhí)行
commitTransactionAfterReturning 方法。最后返回目標(biāo)方法返回值蜒灰。
我們重點(diǎn)看看 completeTransactionAfterThrowing 方法和 commitTransactionAfterReturning 方法弦蹂。
5. TransactionInterceptor 的 completeTransactionAfterThrowing 方法(事務(wù)如何回滾)。
該方法主要內(nèi)容在紅框中强窖,首先判斷該事務(wù)對(duì)象是否和該異常匹配凸椿,如果匹配,則回滾翅溺,否則脑漫,則提交。那么咙崎,是否匹配的邏輯是怎么樣的呢优幸?我們的事務(wù)屬性是什么類型的?RuleBasedTransactionAttribute 褪猛,就是我們剛剛創(chuàng)建解析注解后創(chuàng)建的网杆。那么我就看看該類的 rollbackOn 方法:
首先,循環(huán)解析注解時(shí)添加進(jìn)集合的回滾元素伊滋。并遞歸調(diào)用RollbackRuleAttribute 的 getDepth 方法碳却,如果這個(gè)異常的名字和注解中的異常名字匹配,則返回該異常的回滾類型笑旺。最后判斷昼浦,如果沒有匹配到,則調(diào)用父類的 rollbackOn 方法筒主,如果匹配到了关噪,并且該屬性類型不是 NoRollbackRuleAttribute 類型鸟蟹,返回true。表示匹配到了使兔,可以回滾建钥。那么父類的 rollbackOn 方法肯定就是默認(rèn)的回滾方法了。
這是父類的 rollbackOn 方法:
該方法判斷火诸,該異常如果是 RuntimeException 類型異辰跽耄或者 是 Error 類型的荠察,就回滾置蜀。這就是默認(rèn)的回滾策略。
那么我們的方法肯定是匹配的 RuntimeException 異常悉盆,就會(huì)執(zhí)行下面的方法盯荤。
可以看到,這行代碼就是執(zhí)行了我們的事務(wù)管理器的 rollback 方法焕盟,并且攜帶了事務(wù)狀態(tài)對(duì)象秋秤。該方法實(shí)現(xiàn)在抽象類 AbstractPlatformTransactionManager 中,調(diào)用了自身的 processRollback 方法做真正的實(shí)現(xiàn)脚翘。
該方法首先切換事務(wù)狀態(tài)灼卢,其實(shí)就是關(guān)閉SqlSession。
然后調(diào)用 doRollback 方法来农。
首先鞋真,從狀態(tài)對(duì)象中獲取數(shù)據(jù)庫(kù)連接持有對(duì)象,然后獲取數(shù)據(jù)庫(kù)連接沃于,調(diào)用 Connection 的 rollback 方法涩咖,也就是我們學(xué)習(xí)JDBC 時(shí)使用的方法。最后修改事務(wù)的狀態(tài)繁莹。
到這里檩互,事務(wù)的回滾就結(jié)束了。
那么咨演,事務(wù)時(shí)如何提交的呢闸昨?
6. TransactionInterceptor 的 commitTransactionAfterReturning 方法(事務(wù)如何提交)。
該方法簡(jiǎn)單的調(diào)用了事務(wù)管理器的 commit 方法薄风。
AbstractPlatformTransactionManager 的 commit 方法零院。
首先判斷了事務(wù)的狀態(tài),如果狀態(tài)不匹配村刨,則調(diào)用回滾方法告抄。如果狀態(tài)正常,執(zhí)行 processCommit 方法嵌牺。該方法很長(zhǎng)打洼,樓主只截取其中一段:
首先龄糊,commit 之前做一些狀態(tài)切換工作。最重要的是執(zhí)行 doCommit 方法募疮,如果異常了炫惩,則回滾。那么 DataSourceTransactionManager 的 doCommit 是如何執(zhí)行的呢阿浓?
可以看到他嚷,底層也是調(diào)用 JDBC 的 Connection 的 commit 方法。
到這里芭毙,我們就完成了數(shù)據(jù)庫(kù)的提交筋蓖。
7. 事務(wù)運(yùn)行之前做了哪些工作?
從前面的分析退敦,我們已經(jīng)知道了事務(wù)是如何運(yùn)行的粘咖,如何回滾的,又是如何提交的侈百。在這是交互型的框架里瓮下,事務(wù)系統(tǒng)肯定做了很多的準(zhǔn)備工作,同時(shí)钝域,我們留下了很多的疑問讽坏,比如事務(wù)管理器從何而來? TransactionAttributeSource 屬性何時(shí)生成例证?AnnotationTransactionAttributeSource 構(gòu)造什么時(shí)候調(diào)用路呜?
我們一個(gè)個(gè)的來解釋。
在Spring 中战虏,有一個(gè)現(xiàn)成的類拣宰,ProxyTransactionManagementConfiguration,我們看看該類:
看到這個(gè)類烦感,應(yīng)該可以解開我們的疑惑巡社,這個(gè)類標(biāo)注了配置注解,會(huì)在IOC的時(shí)候?qū)嵗擃愂秩ぃ擃愔挟a(chǎn)生了幾個(gè)Bean晌该,比如事務(wù)攔截器 TransactionInterceptor,創(chuàng)建了 AnnotationTransactionAttributeSource 對(duì)象绿渣,并向事務(wù)攔截器添加了事務(wù)管理器朝群。最后,將事務(wù)攔截器封裝成通知器中符。那么姜胖,剩下最后一個(gè)問題就是,事務(wù)管理器從何而來淀散?答案是他的父類 AbstractTransactionManagementConfiguration :
該類也是個(gè)配置類右莱,自動(dòng)注入了 TransactionManagementConfigurer 的配置集合蚜锨,而并且尋找了配置 EnableTransactionManagement 注解的類,而我們?cè)谖覀兊捻?xiàng)目中就是按照這個(gè)標(biāo)準(zhǔn)來實(shí)現(xiàn)的:
我們關(guān)聯(lián)這兩個(gè)類就能一目了然慢蜓,Spring在啟動(dòng)的時(shí)候亚再,會(huì)加載這兩個(gè)配置類,在對(duì) AbstractTransactionManagementConfiguration 的 setConfigurers 方法進(jìn)行注入的時(shí)候晨抡,會(huì)從容器中找到對(duì)應(yīng)類型的配置氛悬,并調(diào)用配置類的 annotationDrivenTransactionManager 方法,也就是我們實(shí)現(xiàn)的方法耘柱,獲取到我們創(chuàng)建的 DataSourceTransactionManager 類如捅。這樣,我們的事務(wù)攔截器相關(guān)的類就完成了在Spring中的依賴關(guān)系帆谍。
但是伪朽,這個(gè)時(shí)候Spring中的事務(wù)運(yùn)行還沒有搭建完成轴咱。比如什么時(shí)候創(chuàng)建類的代理汛蝙?根據(jù)什么創(chuàng)建代理,因?yàn)槲覀冎榔臃危琒pring 中的事務(wù)就是使用AOP來完成的窖剑,必須使用動(dòng)態(tài)代理或者 Cglib 代理來對(duì)目標(biāo)方法進(jìn)行攔截。
這就要復(fù)習(xí)我們之前的Spring IOC 的啟動(dòng)過程了戈稿。Spring 在創(chuàng)建bean的時(shí)候西土,會(huì)對(duì)每個(gè)Bean 的所有方法進(jìn)行遍歷,如果該方法匹配系統(tǒng)中任何一個(gè)攔截器的切點(diǎn)鞍盗,就創(chuàng)建一個(gè)該Bean的代理對(duì)象需了。并且會(huì)將對(duì)應(yīng)的通知器放入到代理類中。以便在執(zhí)行代理方法的時(shí)候進(jìn)行攔截般甲。
具體代碼步驟樓主貼一下:
- 在對(duì)bean 進(jìn)行初始化的時(shí)候會(huì)執(zhí)行 AutowireCapableBeanFactory 接口的 applyBeanPostProcessorsAfterInitialization 的方法肋乍,其中會(huì)遍歷容器中所有的bean后置處理器,后置處理器會(huì)調(diào)用 postProcessAfterInitialization 方法對(duì)bean進(jìn)行處理敷存。
- 在處理過程中墓造,對(duì)bean 進(jìn)行包裝,也就是代理的創(chuàng)建锚烦,調(diào)用 getAdvicesAndAdvisorsForBean 方法觅闽,該方法會(huì)根據(jù)bean的信息獲取到對(duì)應(yīng)的攔截器并創(chuàng)建代理,創(chuàng)建代理的過程我們之前已經(jīng)分析過了涮俄,不再贅述蛉拙。
- 尋找匹配攔截器過程:首先找到所有的攔截器,然后彻亲,根據(jù)bean的信息進(jìn)行匹配孕锄。
- 匹配的過程就是室叉,找到目標(biāo)類的所有方法,遍歷硫惕,并調(diào)用攔截器的方法匹配器對(duì)每個(gè)方法進(jìn)行匹配茧痕。方法匹配器就是事務(wù)攔截器中的 BeanFactoryTransactionAttributeSourceAdvisor 類,該類封裝了 AnnotationTransactionAttributeSource 用于匹配事務(wù)注解的匹配器恼除。
- 最終調(diào)用方法匹配器中封裝的注解解析器解析方法踪旷,判斷方法是否含有事務(wù)注解從而決定是否生成代理:
到這里,就完成了所有事務(wù)代理對(duì)象的創(chuàng)建豁辉。
項(xiàng)目中的每個(gè)Bean都有了代理對(duì)象令野,在執(zhí)行目標(biāo)方法的時(shí)候,代理類會(huì)查看目標(biāo)方法是否匹配代理中攔截器的方法匹配器中定義的切點(diǎn)徽级。如果匹配气破,則執(zhí)行攔截器的攔截方法,否則餐抢,直接執(zhí)行目標(biāo)方法现使。這就是含有事務(wù)注解和不含有事務(wù)注解方法的執(zhí)行區(qū)別。
到這里旷痕,我們還剩下最后一個(gè)問題碳锈,我們知道,在分析mybatis 的時(shí)候欺抗,mybatis 也有自己的事務(wù)管理器售碳,那么他們?nèi)诤现螅麄兊氖聞?wù)管理權(quán)在誰的手上绞呈,又是根據(jù)什么切換的呢贸人?
8. mybatis 和 Spring 的事務(wù)管理權(quán)力之爭(zhēng)
我們之前說過,在Spring中佃声,mybatis 有 SqlSessionTemplate 代理執(zhí)行艺智,其實(shí)現(xiàn)類動(dòng)態(tài)代理的 InvocationHandler 方法,那么最重要的方法就是 invoke 方法秉溉,其實(shí)這個(gè)方法我們已經(jīng)看過了力惯,今天再看一遍:
我們今天重點(diǎn)關(guān)注是否提交(報(bào)錯(cuò)肯定回滾),其中紅框標(biāo)出來的 if 判斷召嘶,就是判斷這個(gè)事務(wù)到底是Spring 來提交父晶,還是 mybatis 來提交,那么我們看看這個(gè)方法 isSqlSessionTransactional :
該方法從Spring 的容器中取出持有 SqlSession 的 持有類弄跌,判斷Spirng 持有的 SqlSession 和 Mybatis 持有的是否是同一個(gè)甲喝,如果是,則交給Spring铛只,否則埠胖,Mybatis 自己處理糠溜。可以說很合理直撤。
總結(jié)
今天的這篇文章可以說非常的長(zhǎng)非竿,我們分析了 SpringBoot 的事務(wù)運(yùn)行過程,事務(wù)環(huán)境的搭建過程谋竖,mybatis 的事務(wù)和 Spring 事務(wù)如何協(xié)作红柱。知道了整個(gè)事務(wù)其實(shí)是建立在AOP的基礎(chǔ)之上,其核心類就是 TransactionInterceptor蓖乘,該類就是 invokeWithinTransaction 方法是就事務(wù)處理的核心方法锤悄,其中封裝了我們創(chuàng)建的 DataSourceTransactionManager 對(duì)象,該對(duì)象就是執(zhí)行回滾或者提交的執(zhí)行單位 其實(shí)嘉抒,TransactionInterceptor 和我們平時(shí)標(biāo)注 @Aspect 注解的類的作用相同零聚,就是攔截指定的方法,而在
TransactionInterceptor 中是通過是否標(biāo)有事務(wù)注解來決定的些侍。如果一個(gè)類中任意方法含有事務(wù)注解隶症,那么這個(gè)方法就會(huì)被代理。而Mybatis 的事務(wù)和Spring 的事務(wù)協(xié)作則根據(jù)他們的SqlSession 是否是同一個(gè)SqlSession 來決定的娩梨,如果是同一個(gè)沿腰,則交給Spring览徒,如果不是狈定,Mybatis 則自己處理。
通過閱讀源碼习蓬,我們已經(jīng)弄清楚了SpirngBoot 整個(gè)事務(wù)的運(yùn)行過程纽什。實(shí)際上,Spring 的其他版本也大同小異躲叼。底層都是 TransactionInterceptor 芦缰,只不過入口不一樣。我相信枫慷,在以后的工作中让蕾,如果遇到了Spring事務(wù)相關(guān)的問題,再也不會(huì)感到無助了或听,因?yàn)橹懒嗽硗叮梢陨钊氲皆创a中查看吝沫。
到這里,樓主的 Spring ,mybatis 默垄,Tomcat 的源碼閱讀之路暫時(shí)就告一段落了。源碼只要領(lǐng)會(huì)精華即可。還有其他的知識(shí)需要花費(fèi)更多的時(shí)間學(xué)習(xí)。比如并發(fā)庇配,JVM.
good luck!I苄@袒拧!