1. 程式人生 > 其它 >spring成神之路第四十八篇:@Transaction 事務原始碼解析

spring成神之路第四十八篇:@Transaction 事務原始碼解析

大家好,今天咱們通過原始碼來了解一下spring中@Transaction事務的原理。

開始本文之前,下面這些知識需提前瞭解下

1、吃透Spring AOP

2、Spring程式設計式事務原始碼解析

在這裡插播兩句,整個系列前後知識是有依賴的,大家最好按順序閱讀,這樣不會出現無法理解的情況,若跳著讀,可能會比較懵。。。

1、環境

  1. jdk1.8
  2. Spring版本:5.2.3.RELEASE
  3. mysql5.7

2、@Transaction 事務的用法

咱們先來回顧一下,@Transaction 事務的用法,特別簡單,2個步驟

1、在需要讓spring管理事務的方法上新增 @Transaction

註解

2、在spring配置類上新增 @EnableTransactionManagement 註解,這步特別重要,別給忘了,有了這個註解之後,@Trasaction標註的方法才會生效

3、@Transaction事務原理

原理比較簡單,內部是通過spring aop的功能,通過攔截器攔截 @Transaction 方法的執行,在方法前後新增事務的功能。

4、@EnableTransactionManagement註解作用

@EnableTransactionManagement註解會開啟spring自動管理事務的功能,有了這個註解之後,spring容器啟動的過程中,會攔截所有bean的建立過程,判斷bean 是否需要讓spring來管理事務,即判斷bean中是否有@Transaction

註解,判斷規則如下

1、一直沿著當前bean的類向上找,先從當前類中,然後父類、父類的父類,當前類的介面、介面父介面,父介面的父介面,一直向上找,一下這些型別上面是否有 @Transaction註解

2、類的任意public方法上面是否有@Transaction註解

如果bean滿足上面任意一個規則,就會被spring容器通過aop的方式建立代理,代理中會新增一個攔截器

org.springframework.transaction.interceptor.TransactionInterceptor

TransactionInterceptor 攔截器是關鍵,它會攔截@Trasaction方法的執行,在方法執行前後新增事務的功能,這個攔截器中大部分都是程式設計式事務的程式碼,若

程式設計式事務的原始碼 大家看懂了,這個攔截器原始碼看起來就是小兒科了。

5、@EnableTransactionManagement原始碼解析

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)//@1
public@interfaceEnableTransactionManagement{

//是基於類的代理(cglib),還是基於介面的代理(jdk動態代理),預設false,表示是基於jdk動態代理
booleanproxyTargetClass()defaultfalse;

//通知的模式,預設是通過aop的方式
AdviceModemode()defaultAdviceMode.PROXY;

//我們知道這個註解的功能最終是通過aop的方式來實現的,對bean建立了一個代理,代理中添加了一個攔截器
//當代理中還有其他攔截器的是時候,可以通過order這個屬性來指定事務攔截器的順序
//預設值是LOWEST_PRECEDENCE=Integer.MAX_VALUE,攔截器的執行順序是order升序
intorder()defaultOrdered.LOWEST_PRECEDENCE;

}

注意@1這個程式碼

@Import(TransactionManagementConfigurationSelector.class)

用到了@Import註解,對這個註解不熟悉的可以看一下Spring系列第18篇:@import詳解(bean批量註冊),這個註解的value是TransactionManagementConfigurationSelector,看一下這個類的原始碼,重點是他的selectImports方法,這個方法會返回一個類名陣列,spring容器啟動過程中會自動呼叫這個方法,將這個方法指定的類註冊到spring容器中;方法的引數是AdviceMode,這個就是@EnableTransactionManagement註解中mode屬性的值,預設是PROXY,所以會走到@1程式碼處

publicclassTransactionManagementConfigurationSelectorextendsAdviceModeImportSelector<EnableTransactionManagement>{

@Override
protectedString[]selectImports(AdviceModeadviceMode){
switch(adviceMode){
casePROXY://@1
returnnewString[]{AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
caseASPECTJ:
returnnewString[]{determineTransactionAspectClass()};
default:
returnnull;
}
}

}

最終會在spirng容器中註冊下面這2個bean

AutoProxyRegistrar
ProxyTransactionManagementConfiguration

下面來看一下這2個類的程式碼。

AutoProxyRegistrar

這個類實現了ImportBeanDefinitionRegistrar介面,這個介面中有個方法registerBeanDefinitions,spring容器在啟動過程中會呼叫這個方法,開發者可以在這個方法中做一些bean註冊的事情,而AutoProxyRegistrar在這個方法中主要做的事情就是下面@1的程式碼,大家可以點進去看看,這裡我就不點進去了,這個程式碼的作用就是在容器中做了一個非常關鍵的bean:InfrastructureAdvisorAutoProxyCreator,這個類之前在aop中介紹過,是bean後置處理器,會攔截所有bean的建立,對符合條件的bean建立代理。

publicclassAutoProxyRegistrarimplementsImportBeanDefinitionRegistrar{

@Override
publicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){
booleancandidateFound=false;
Set<String>annTypes=importingClassMetadata.getAnnotationTypes();
for(StringannType:annTypes){
AnnotationAttributescandidate=AnnotationConfigUtils.attributesFor(importingClassMetadata,annType);
if(candidate==null){
continue;
}
Objectmode=candidate.get("mode");
ObjectproxyTargetClass=candidate.get("proxyTargetClass");
if(mode!=null&&proxyTargetClass!=null&&AdviceMode.class==mode.getClass()&&
Boolean.class==proxyTargetClass.getClass()){
candidateFound=true;
if(mode==AdviceMode.PROXY){
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);//@1
if((Boolean)proxyTargetClass){
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
}
}

說的簡單點:AutoProxyRegistrar的作用就是啟用spring aop的功能,對符合條件的bean建立代理。

ProxyTransactionManagementConfiguration
@Configuration(proxyBeanMethods=false)
publicclassProxyTransactionManagementConfigurationextendsAbstractTransactionManagementConfiguration{
//註冊bean:事務顧問(spring aop中攔截器鏈就是一個個的Advisor物件)
@Bean(name=TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicBeanFactoryTransactionAttributeSourceAdvisortransactionAdvisor(
TransactionAttributeSourcetransactionAttributeSource,
TransactionInterceptortransactionInterceptor)
{
BeanFactoryTransactionAttributeSourceAdvisoradvisor=newBeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
//設定事務攔截器
advisor.setAdvice(transactionInterceptor);
if(this.enableTx!=null){
//設定aop中事務攔截器的順序
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
returnadvisor;
}

//註冊bean:TransactionAttributeSource,TransactionAttributeSource用來獲取獲取事務屬性配置資訊:TransactionAttribute
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicTransactionAttributeSourcetransactionAttributeSource(){//@1
returnnewAnnotationTransactionAttributeSource();
}

//註冊bean:事務攔截器
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicTransactionInterceptortransactionInterceptor(
TransactionAttributeSourcetransactionAttributeSource)
{
TransactionInterceptorinterceptor=newTransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
//攔截器中設定事務管理器,txManager可以為空
if(this.txManager!=null){
interceptor.setTransactionManager(this.txManager);
}
returninterceptor;
}

}

是個配置類,程式碼比較簡單,註冊了3個bean,最重要的一點就是添加了事務事務攔截器:TransactionInterceptor。

AutoProxyRegistrar負責啟用aop的功能,而ProxyTransactionManagementConfiguration負責在aop中新增事務攔截器,二者結合起來的效果就是:對@Transaction標註的bean建立代理物件,代理物件中通過TransactionInterceptor攔截器來實現事務管理的功能。

再看下程式碼@1,註冊了一個TransactionAttributeSource型別的bean

TransactionAttributeSource介面原始碼:

publicinterfaceTransactionAttributeSource{

/**
*確定給定的類是否是這個TransactionAttributeSource元資料格式中的事務屬性的候選類。
*如果此方法返回false,則不會遍歷給定類上的方法,以進行getTransactionAttribute內省。
*因此,返回false是對不受影響的類的優化,而返回true僅僅意味著類需要對給定類上的每個方法進行完全自省。
**/

defaultbooleanisCandidateClass(Class<?>targetClass){
returntrue;
}

//返回給定方法的事務屬性,如果該方法是非事務性的,則返回null。
TransactionAttributegetTransactionAttribute(Methodmethod,@NullableClass<?>targetClass);

}

getTransactionAttribute方法用來獲取指定方法上的事務屬性資訊TransactionAttribute,大家對TransactionDefinition比較熟悉吧,用來配置事務屬性資訊的,而TransactionAttribute繼承了TransactionDefinition介面,原始碼如下,而TransactionAttribute中新定義了2個方法,一個方法用來指定事務管理器bean名稱的,一個用來判斷給定的異常是否需要回滾事務

publicinterfaceTransactionAttributeextendsTransactionDefinition{

//事務管理器的bean名稱
@Nullable
StringgetQualifier();

//判斷指定的異常是否需要回滾事務
booleanrollbackOn(Throwableex);

}

TransactionAttributeSource介面有個實現類AnnotationTransactionAttributeSource,負責將@Transaction解析為TransactionAttribute物件,大家可以去這個類中設定一下斷點看一下@Transaction註解查詢的順序,這樣可以深入理解@Transaction放在什麼地方才會讓事務起效。

AnnotationTransactionAttributeSource內部最會委託給SpringTransactionAnnotationParser#parseTransactionAnnotation方法來解析@Transaction註解,進而得到事務屬性配置資訊:RuleBasedTransactionAttribute,程式碼如下:

org.springframework.transaction.annotation.SpringTransactionAnnotationParser

protectedTransactionAttributeparseTransactionAnnotation(AnnotationAttributesattributes)
{
RuleBasedTransactionAttributerbta=newRuleBasedTransactionAttribute();

Propagationpropagation=attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolationisolation=attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));

//回滾規則
List<RollbackRuleAttribute>rollbackRules=newArrayList<>();
for(Class<?>rbRule:attributes.getClassArray("rollbackFor")){
rollbackRules.add(newRollbackRuleAttribute(rbRule));
}
for(StringrbRule:attributes.getStringArray("rollbackForClassName")){
rollbackRules.add(newRollbackRuleAttribute(rbRule));
}
for(Class<?>rbRule:attributes.getClassArray("noRollbackFor")){
rollbackRules.add(newNoRollbackRuleAttribute(rbRule));
}
for(StringrbRule:attributes.getStringArray("noRollbackForClassName")){
rollbackRules.add(newNoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);

returnrbta;
}

下面來看重點了事務攔截器。

6、TransactionInterceptor

負責攔截@Transaction方法的執行,在方法執行之前開啟spring事務,方法執行完畢之後提交或者回滾事務。

在講這個類的原始碼之前,先提幾個問題,大家帶著問題去看程式碼,理解更深一些。

1、事務管理器是如何獲取的?

2、什麼情況下事務會提交?

3、什麼異常會導致事務回滾?

6.1、invokeWithinTransaction方法

這個方法是事務攔截器的入口,需要spring管理事務的業務方法會被這個方法攔截,大家可以設定斷點跟蹤一下

protectedObjectinvokeWithinTransaction(Methodmethod,@NullableClass<?>targetClass,
finalInvocationCallbackinvocation)
throwsThrowable
{

TransactionAttributeSourcetas=getTransactionAttributeSource();
//@6-1:獲取事務屬性配置資訊:通過TransactionAttributeSource.getTransactionAttribute解析@Trasaction註解得到事務屬性配置資訊
finalTransactionAttributetxAttr=(tas!=null?tas.getTransactionAttribute(method,targetClass):null);
//@6-2:獲取事務管理器
finalTransactionManagertm=determineTransactionManager(txAttr);

//將事務管理器tx轉換為PlatformTransactionManager
PlatformTransactionManagerptm=asPlatformTransactionManager(tm);

if(txAttr==null||!(ptminstanceofCallbackPreferringPlatformTransactionManager)){
//createTransactionIfNecessary內部,這裡就不說了,內部主要就是使用spring事務硬編碼的方式開啟事務,最終會返回一個TransactionInfo物件
TransactionInfotxInfo=createTransactionIfNecessary(ptm,txAttr,joinpointIdentification);
//業務方法返回值
ObjectretVal;
try{
//呼叫aop中的下一個攔截器,最終會呼叫到業務目標方法,獲取到目標方法的返回值
retVal=invocation.proceedWithInvocation();
}
catch(Throwableex){
//6-3:異常情況下,如何走?可能只需提交,也可能只需回滾,這個取決於事務的配置
completeTransactionAfterThrowing(txInfo,ex);
throwex;
}
finally{
//清理事務資訊
cleanupTransactionInfo(txInfo);
}
//6-4:業務方法返回之後,只需事務提交操作
commitTransactionAfterReturning(txInfo);
//返回執行結果
returnretVal;
}
}

6.2、獲取事務管理器

//@6-2:獲取事務管理器
finalTransactionManagertm=determineTransactionManager(txAttr);

determineTransactionManager原始碼如下:

org.springframework.transaction.interceptor.TransactionAspectSupport#determineTransactionManager

protectedTransactionManagerdetermineTransactionManager(@NullableTransactionAttributetxAttr)
{
//txAttr==null||this.beanFactory==null,返回攔截器中配置的事務管理器
if(txAttr==null||this.beanFactory==null){
returngetTransactionManager();
}

//qualifier就是@Transactional註解中通過value或者transactionManager來指定事務管理器的bean名稱
Stringqualifier=txAttr.getQualifier();
if(StringUtils.hasText(qualifier)){
//從spring容器中查詢[beanName:qualifier,type:TransactionManager]的bean
returndetermineQualifiedTransactionManager(this.beanFactory,qualifier);
}
elseif(StringUtils.hasText(this.transactionManagerBeanName)){
//從spring容器中查詢[beanName:this.transactionManagerBeanName,type:TransactionManager]的bean
returndetermineQualifiedTransactionManager(this.beanFactory,this.transactionManagerBeanName);
}
else{
//最後通過型別TransactionManager在spring容器中找事務管理器
TransactionManagerdefaultTransactionManager=getTransactionManager();
if(defaultTransactionManager==null){
defaultTransactionManager=this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
if(defaultTransactionManager==null){
defaultTransactionManager=this.beanFactory.getBean(TransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY,defaultTransactionManager);
}
}
returndefaultTransactionManager;
}
}

從上面可知,事務管理器的查詢順序:

1、先看@Transactional中是否通過value或者transactionManager指定了事務管理器

2、TransactionInterceptor.transactionManagerBeanName是否有值,如果有,將通過這個值查詢事務管理器

3、如果上面2種都沒有,將從spring容器中查詢TransactionManager型別的事務管理器

6.3、異常情況下,如何走?

try{
//....
}catch(Throwableex){
//6-3:異常情況下,如何走?可能只需提交,也可能只需回滾,這個取決於事務的配置
completeTransactionAfterThrowing(txInfo,ex);
throwex;
}

原始碼中可以看出,發生異常了會進入completeTransactionAfterThrowing方法,completeTransactionAfterThrowing 原始碼如下

protectedvoidcompleteTransactionAfterThrowing(@NullableTransactionInfotxInfo,Throwableex){
if(txInfo!=null&&txInfo.getTransactionStatus()!=null){
//@6-3-1:判斷事務是否需要回滾
if(txInfo.transactionAttribute!=null&&txInfo.transactionAttribute.rollbackOn(ex)){
//通過事務管理器回滾事務
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
else{
//通過事務管理器提交事務
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
}

注意上面的@6-3-1程式碼,判斷事務是否需要回滾,呼叫的是transactionAttribute.rollbackOn(ex),最終會進入下面這個方法內部

org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn

publicbooleanrollbackOn(Throwableex)
{
RollbackRuleAttributewinner=null;
intdeepest=Integer.MAX_VALUE;

//@Trasaction中可以通過rollbackFor指定需要回滾的異常列表,通過noRollbackFor屬性指定不需要回滾的異常
//根據@Transactional中指定的回滾規則判斷ex型別的異常是否需要回滾
if(this.rollbackRules!=null){
for(RollbackRuleAttributerule:this.rollbackRules){
intdepth=rule.getDepth(ex);
if(depth>=0&&depth<deepest){
deepest=depth;
winner=rule;
}
}
}
//若@Transactional註解中沒有匹配到,這走預設的規則,將通過super.rollbackOn來判斷
if(winner==null){
returnsuper.rollbackOn(ex);
}

return!(winnerinstanceofNoRollbackRuleAttribute);
}

super.rollbackOn(ex)原始碼如下,可以看出預設情況下,異常型別是RuntimeException或者Error的情況下,事務才會回滾

@Override
publicbooleanrollbackOn(Throwableex){
return(exinstanceofRuntimeException||exinstanceofError);
}

6.4、沒有異常如何走?

//6-4:業務方法返回之後,只需事務提交操作
commitTransactionAfterReturning(txInfo);

沒有異常的情況下會進入commitTransactionAfterReturning方法,commitTransactionAfterReturning原始碼如下,比較簡單,就是呼叫事務管理器的commit方法提交事務

protectedvoidcommitTransactionAfterReturning(@NullableTransactionInfotxInfo){
if(txInfo!=null&&txInfo.getTransactionStatus()!=null){
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}

原始碼解析的差不多了,建議大家設定斷點跟蹤一下,加深對事務原理的理解。

7、重點回顧

1、使用@Transaction的時候,一定別忘記@EnableTransactionManagement註解,否則事務不起效

2、@Transaction的功能主要是通過aop來實現的,關鍵程式碼在TransactionInterceptor攔截器中

3、預設情況下,事務只會在 RuntimeException 或 Error 異常下回滾,可以通@Transaction來配置其他需要回滾或不需要回滾的異常型別

8、課堂討論

下面程式碼的執行結果是什麼?為什麼?

@Component
publicclassServiceA{

@Autowired
ServiceBserviceB;

@Transactional
publicvoidm1(){
try{
serviceB.m2();
}catch(Exceptione){
e.printStackTrace();
}
"insertintot_user(name)values('張三')";
}
}

@Component
publicclassServiceB{

@Transactional
publicvoidm2(){
"insertintot_user(name)values('李四')";
thrownewRuntimeException("手動丟擲異常!");
}
}

歡迎留言和我分享你的想法。如果有收穫,也歡迎你把這篇文章分享給你的朋友。

來源:https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648937715&idx=2&sn=2d8534f9788bfa4678554d858ec93ab3&scene=21#wechat_redirect