spring成神之路第四十八篇:@Transaction 事务源码解析

技术spring成神之路第四十八篇:@Transaction 事务源码解析 spring成神之路第四十八篇:@Transaction 事务源码解析大家好,今天咱们通过源码来了解一下spring中@Tra

春天的上帝之路第48章:分析@交易交易源代码

大家好。今天,让我们通过源代码了解一下@Transaction在春天的原理。

在开始这篇文章之前,需要提前了解以下知识。

1.彻底了解Spring AOP

2、Spring编程式事务源码解析

在这里插播两句,整个系列前后知识是有依赖的,大家最好按顺序阅读,这样不会出现无法理解的情况,若跳着读,可能会比较懵。。。

1、环境

jdk1.8

春季版本:5.2.3.RELEASE

mysql5.7

2、@Transaction 事务的用法

让我们首先回顾一下@Transaction事务的用法,它非常简单,有两个步骤。

1.将@Transaction注释添加到需要spring来管理事务的方法中。

2.将@EnableTransactionManagement的注释添加到spring配置类中。这一步尤为重要。别忘了,有了这个注解,由@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代码处

publicclassTransactionManagementConfigurationSelectorextendsAdviceModeImportSelectorEnableTransactionManagement{

@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;
SetStringannTypes=importingClassMetadata.getAnnotationTypes();
for(StringannType:annTypes){
AnnotationAttributescandidate=AnnotationConfigUtils.attributesFor(importingClassMetadata,annType);
if(candidate==null){
continue;
}
Objectmode=candidate.get("mode");
ObjectproxyTargetClass=candidate.get("proxyTargetClass");
if(mode!=nullproxyTargetClass!=nullAdviceMode.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.IntegergetNumber("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(ClasstargetClass){
returntrue;
}

//返回给定方法的事务属性,如果该方法是非事务性的,则返回null。
TransactionAttributegetTransactionAttribute(Methodmethod,@NullableClasstargetClass);

}

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"));

//回滚规则
ListRollbackRuleAttributerollbackRules=newArrayList();
for(ClassrbRule:attributes.getClassArray("rollbackFor")){
rollbackRules.add(newRollbackRuleAttribute(rbRule));
}
for(StringrbRule:attributes.getStringArray("rollbackForClassName")){
rollbackRules.add(newRollbackRuleAttribute(rbRule));
}
for(ClassrbRule: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,@NullableClasstargetClass,
finalInvocationCallbackinvocation)throwsThrowable{

TransactionAttributeSourcetas=getTransactionAttributeSource();
//@6-1:获取事务属性配置信息:通过TransactionAttributeSource.getTransactionAttribute解析@Trasaction注解得到事务属性配置信息
finalTransactionAttributetxAttr=(tas!=nulltas.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!=nulltxInfo.getTransactionStatus()!=null){
//@6-3-1:判断事务是否需要回滚
if(txInfo.transactionAttribute!=nulltxInfo.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=0depthdeepest){
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!=nulltxInfo.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=2648937715idx=2sn=2d8534f9788bfa4678554d858ec93ab3scene=21#wechat_redirect

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/74741.html

(0)

相关推荐

  • 家具品牌排行榜,全国十大名牌家具的排名

    技术家具品牌排行榜,全国十大名牌家具的排名网上复制的家具品牌排行榜,希望对你有用啊,呵呵。 中国十大木地板品牌排行榜: 01,圣象地板(中国驰名商标,中国名牌)圣象集团; 02,盈彬大自然(中国名牌,国家免检产品)佛山

    生活 2021年10月28日
  • 高铁一等座和二等座的区别,一等座二等座是高铁还是火车

    技术高铁一等座和二等座的区别,一等座二等座是高铁还是火车高铁一等座和二等座的区别主要是在舒适性、设备配置方面,还有票价方面不同高铁一等座和二等座的区别。1、一等座和二等座首先是坐席不同,高铁一等座四个人一排,每座下面都有

    生活 2021年10月29日
  • 羊绒衫怎么洗最好,羊绒衫染了用什么方法可以洗掉

    技术羊绒衫怎么洗最好,羊绒衫染了用什么方法可以洗掉清洗羊绒衫前羊绒衫怎么洗最好,先注意羊绒衫的衣标,尤其是衣服的成分,清洗方式,水温及晾干方法,不要因为操作失误,浪费一件衣服。 将洗衣盆内接好清水,水温控制在35度以下,

    生活 2021年10月28日
  • Spring Boot:整合knife4j

    技术Spring Boot:整合knife4j Spring Boot:整合knife4j前言
    这玩意就swagger的升级版,但是用起来比swagger舒服些,界面也看着好看。
    knife4j是为Ja

    礼包 2021年12月18日
  • 网络协议tcp和udp(tcp和udp协议通过什么区分)

    技术网络协议TCP和UDP怎么理解这篇文章主要讲解了“网络协议TCP和UDP怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“网络协议TCP和UDP怎么理解”吧!国际

    攻略 2021年12月21日
  • 什么是Inception以及GoogleNet结构

    技术什么是Inception以及GoogleNet结构什么是Inception以及GoogleNet结构,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。g

    攻略 2021年11月23日