您的位置:首页 > 新闻 > 热点要闻 > 【源码】SpringBoot编程式事务使用及执行原理

【源码】SpringBoot编程式事务使用及执行原理

2024/10/31 13:08:33 来源:https://blog.csdn.net/JingAi_jia917/article/details/139626957  浏览:    关键词:【源码】SpringBoot编程式事务使用及执行原理

Spring事务

1、【源码】SpringBoot事务注册原理

2、【源码】Spring Data JPA原理解析之事务注册原理

3、【源码】Spring Data JPA原理解析之事务执行原理

4、【源码】SpringBoot编程式事务使用及执行原理

5、【源码】Spring事务之传播特性的详解

6、【源码】Spring事务之事务失效及原理

前言

《Spring事务系列》的前面几篇博文讲解了通过@Transactional注解实现事务的原理,在Spring中,还有另外一种方式可以实现事务,那就是接下去要分享的编程式事务。在开始讲解编程式事务之前,先来看一个编程式事务的例子。

编程式事务示例

在使用编程式事务时,同样需要在项目中添加@EnableTransactionManagement注解,对于SpringBoot项目,默认已经添加了@EnableTransactionManagement注解。示例如下:

@Service
public class MemberStatisticsService {@Resourceprivate TransactionTemplate transactionTemplate;@Resourceprivate MemberStatisticsRepository memberStatisticsRepository;public int addStatistics(MemberStatisticsEntity entity) {// 省略其他// 开启编程式事务boolean rs = transactionTemplate.execute((status) -> {// 省略其他memberStatisticsRepository.save(entity);return true;});return rs ? 1 : 0;}}

使用编程式事务只需两步:

1)引入TransactionTemplate;

2)在需要使用事务的方法中,只需transactionTemplete.execute()方法,在方法内部的回调方法中,添加需要事务保护的数据库相关操作的业务代码即可;

针对以上的示例,可能有人会想,把其中需要事务保护的数据库操作的业务代码剥离为独立的方法,在方法中添加@Transactional注解,在原方法中调用不就可以了嘛。示例如下:

@Service
public class MemberStatisticsService {@Resourceprivate TransactionTemplate transactionTemplate;@Resourceprivate MemberStatisticsRepository memberStatisticsRepository;public int addStatistics(MemberStatisticsEntity entity) {// 省略其他// 开启事务boolean rs = addStatisticsBs(entity);return rs ? 1 : 0;}@Transactionalpublic int addStatisticsBs(MemberStatisticsEntity entity) {// 省略其他memberStatisticsRepository.save(entity);return true;}}

如果这样写,当addStatisticsBs()方法报错的时候,会发现事务并没有回滚,这到底是为何呢?要回答这个问题,还得从源码说起。

事务失效原因

【源码】SpringBoot事务注册原理-CSDN博客

在上面的博客中介绍了方法中添加@Transactional注解时,该类会生成代理类,代理类中添加了TransactionInterceptor拦截器,从而实现了事务管理。

当addStatistics()方法执行时,会先执行ReflectiveMethodInvocation.proceed()方法,循环遍历所有的拦截器。执行完所有拦截器之后,再执行动态代理对象的target类的对应方法,即原方法。详见:

【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码-CSDN博客

博客中的动态代理方法拦截部分。

因为addStatistics()没有添加@Transactional注解,所以执行target的addStatistics()方法,所以在addStatistics()方法内部的this对象是target,而不是代理对象。所以在addStatistics()内部调用addStatisticsBs()方法时,是执行target的addStatisticsBs()方法,所以不再先执行ReflectiveMethodInvocation.proceed(),也就不会执行TransactionInterceptor拦截器,所以没有开启事务管理。

编程式事务的优缺点

3.1 优点

1)灵活性强:开发人员可以在代码中根据具体业务需要来控制事务的范围,特别是大事务或高并发场景,可以缩小事务范围;

2)易于调试:由于事务管理在代码层实现,可以容易的追踪事务管理的细节;

3.2 缺点

1)代码复杂度高:需要手动处理事务,可能增加工作量和代码的复杂度;

2)实现复杂度较高:事务的范围和属性信息需要在代码中显示声明,可能导致一些特定的业务难以满足;

编程事务实现原理

3.1 TransactionTemplate的注入

SpringBoot启动的时候,在TransactionAutoConfiguration配置类中,自动注入TransactionTemplate。代码如下:

public class TransactionAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnSingleCandidate(PlatformTransactionManager.class)public static class TransactionTemplateConfiguration {@Bean@ConditionalOnMissingBean(TransactionOperations.class)public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {return new TransactionTemplate(transactionManager);}}}

在SpringBoot框架中,会自动引入TransactionAutoConfiguration。且在META-INF的spring-autoconfigure-metadata.properties有如下配置:

org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration=
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration=
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration.ConditionalOnSingleCandidate=org.springframework.transaction.PlatformTransactionManager

说明在自动引入TransactionAutoConfiguration时,先引入内部类TransactionAutoConfiguration。

3.2 TransactionTemplate

TransactionTemplate的代码如下:

package org.springframework.transaction.support;@SuppressWarnings("serial")
public class TransactionTemplate extends DefaultTransactionDefinitionimplements TransactionOperations, InitializingBean {/** Logger available to subclasses */protected final Log logger = LogFactory.getLog(getClass());@Nullableprivate PlatformTransactionManager transactionManager;public TransactionTemplate() {}public TransactionTemplate(PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {super(transactionDefinition);this.transactionManager = transactionManager;}public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}@Nullablepublic PlatformTransactionManager getTransactionManager() {return this.transactionManager;}@Overridepublic void afterPropertiesSet() {if (this.transactionManager == null) {throw new IllegalArgumentException("Property 'transactionManager' is required");}}/*** 执行事务*/@Override@Nullablepublic <T> T execute(TransactionCallback<T> action) throws TransactionException {Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);}else {// 获取一个TransactionStatus对象,此实现处理传播行为TransactionStatus status = this.transactionManager.getTransaction(this);T result;try {// 执行入参action的方法,即业务处理方法result = action.doInTransaction(status);}catch (RuntimeException | Error ex) { // 处理RuntimeException和Error的异常,执行回滚,抛对应异常// Transactional code threw application exception -> rollbackrollbackOnException(status, ex);throw ex;}catch (Throwable ex) { // 处理Throwable异常,也会执行回滚,抛UndeclaredThrowableException异常// Transactional code threw unexpected exception -> rollbackrollbackOnException(status, ex);throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");}this.transactionManager.commit(status);return result;}}/*** 执行异常回滚*/private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");logger.debug("Initiating transaction rollback on application exception", ex);try {// 执行回滚this.transactionManager.rollback(status);}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by rollback exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}}@Overridepublic boolean equals(Object other) {return (this == other || (super.equals(other) && (!(other instanceof TransactionTemplate) ||getTransactionManager() == ((TransactionTemplate) other).getTransactionManager())));}}

3.2.1 execute()的核心功能如下:
1)执行TransactionManager.getTransaction(),实际执行AbstractPlatformTransactionManager.getTransaction()方法。根据事务的传播行为,开启事务,获取TransactionStatus对象,详见

【源码】Spring Data JPA原理解析之事务执行原理-CSDN博客

2)调用action.doInTransaction(status),执行入参action的方法,即业务处理方法。业务处理方法传入的参数为TransactionStatus对象;

3)如果业务方法抛RuntimeException或Error类型的异常,则执行回滚,抛对应异常;

4)如果业务方法抛Throwable异常,也执行回滚,抛UndeclaredThrowableException异常;

这是和通过@Transactional注解实现事务时处理的差异,在@Transactional注解实现的事务异常时,默认只处理RuntimeException或Error类型的异常,或者是注解中声明的回滚信息。

5)如果业务方法没有抛异常,则执行TransactionManager.commit()方法;

3.2.2 rollbackOnException()方法中直接调用TransactionManager.rollback(),执行回滚。

业务异常捕获及回滚

通过前面的分析,不管是通过@Transactional注解实现事务还是编程式事务,都是在业务逻辑出现异常时,事务处理会捕获异常,并判断是否要进行事务回滚,然后抛出对应异常。在某些场景中,需要在业务中自己捕获异常,此时就需要使用别的方式实现事务的回滚。

因为业务中自己捕获了异常,所以在事务管理中,会执行事务的提交。事务提交的源码如下:

package org.springframework.transaction.support;@SuppressWarnings("serial")
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {@Overridepublic final void commit(TransactionStatus status) throws TransactionException {if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 如果本地代码设置了回滚if (defStatus.isLocalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Transactional code has requested rollback");}processRollback(defStatus, false);return;}// 全局事务被标记为仅回滚,但事务代码请求提交if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");}processRollback(defStatus, true);return;}// 提交处理processCommit(defStatus);}}

在commit()方法中,在真正提交处理前,会先进行两个判断:

1)defStatus.isLocalRollbackOnly()如果返回true,会执行回滚;

isLocalRollbackOnly()默认返回false,可以通过setRolbackOnly()修改为true。

在TransactionAspectSupport类中,有一个currentTransactionStatus()的静态方法,通过该方法就可以获取到TransactionStatus对象,调用setRollbackOnly()即可。

注:该方法也适用于使用@Transactional注解的方法

2)判断!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly(),如果为true,也会执行回滚;

结尾

本篇就先分享到这里。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com