Spring事务
2、【源码】Spring Data JPA原理解析之事务注册原理
3、【源码】Spring Data JPA原理解析之事务执行原理
前言
《Spring事务系列》的前面几篇博文讲解了通过@Transactional注解实现事务的原理,在Spring中,还有另外一种方式可以实现事务,那就是接下去要分享的编程式事务。在开始讲解编程式事务之前,先来看一个编程式事务的例子。
编程式事务示例
在使用编程式事务时,同样需要在项目中添加@EnableTransactionManagement注解,对于SpringBoot项目,默认已经添加了@EnableTransactionManagement注解。示例如下:
java
@Service
public class MemberStatisticsService {
@Resource
private TransactionTemplate transactionTemplate;
@Resource
private 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注解,在原方法中调用不就可以了嘛。示例如下:
java
@Service
public class MemberStatisticsService {
@Resource
private TransactionTemplate transactionTemplate;
@Resource
private MemberStatisticsRepository memberStatisticsRepository;
public int addStatistics(MemberStatisticsEntity entity) {
// 省略其他
// 开启事务
boolean rs = addStatisticsBs(entity);
return rs ? 1 : 0;
}
@Transactional
public int addStatisticsBs(MemberStatisticsEntity entity) {
// 省略其他
memberStatisticsRepository.save(entity);
return true;
}
}
如果这样写,当addStatisticsBs()方法报错的时候,会发现事务并没有回滚,这到底是为何呢?要回答这个问题,还得从源码说起。
事务失效原因
在上面的博客中介绍了方法中添加@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。代码如下:
java
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有如下配置:
XML
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的代码如下:
java
package org.springframework.transaction.support;
@SuppressWarnings("serial")
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private 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;
}
@Nullable
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
@Override
public void afterPropertiesSet() {
if (this.transactionManager == null) {
throw new IllegalArgumentException("Property 'transactionManager' is required");
}
}
/**
* 执行事务
*/
@Override
@Nullable
public <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 -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) { // 处理Throwable异常,也会执行回滚,抛UndeclaredThrowableException异常
// Transactional code threw unexpected exception -> rollback
rollbackOnException(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;
}
}
@Override
public 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对象,详见
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注解实现事务还是编程式事务,都是在业务逻辑出现异常时,事务处理会捕获异常,并判断是否要进行事务回滚,然后抛出对应异常。在某些场景中,需要在业务中自己捕获异常,此时就需要使用别的方式实现事务的回滚。
因为业务中自己捕获了异常,所以在事务管理中,会执行事务的提交。事务提交的源码如下:
java
package org.springframework.transaction.support;
@SuppressWarnings("serial")
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
@Override
public 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,也会执行回滚;
结尾
本篇就先分享到这里。
关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。