目录
2.1 createTransactionIfNecessary方法
2.1.3 prepareTransactionInfo方法
2.3 completeTransactionAfterThrowing方法
2.4 commitTransactionAfterReturning方法
@Transactional声明式事务的具体使用方法这里不再重复说明,大家可以参考上一篇文章;
本文主要是带着大家一起看一下@Transactional注解的源码,包括传播机制的实现;
测试案例:下面这两个方法是不同类之间方法调用,并且都加了@Transactional注解的;
@Transactional
public void methodA(){
userMapper.selectById("jzcs");
infoService.methodB("userId");
}
//methodB是infoService的方法
@Transactional
public void methodB(String userId){
infoMapper.selectUserClassInfo(userId);
}
我们知道@Transactional是基于aop实现的,那么必定有拦截器和切面;
1.TransactionInterceptor实现拦截
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
Method var10001 = invocation.getMethod();
invocation.getClass();
return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
}
}
TransactionInterceptor会拦截加了@transactional注解的方法,再调用目标方法前,会调用TransactionAspectSupport的invokeWithinTransaction方法对目标方法进行争强;
2.TransactionAspectSupport切面
既然是基于aop,肯定少不了切面对目标方法进行增强了,TransactionAspectSupport.invokeWithinTransaction方法就是对目标前,创建事务和数据库连接,在目标方法执行后,提供事务的提交和回滚,我们看下面源码大家就清楚了:
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
//获取@Transactional注解定义的的一些属性(包括隔离级别,传播机制等)
TransactionAttributeSource tas = this.getTransactionAttributeSource();
TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
//创建一个dataSourceTransactionManager,包含数据库信息等
PlatformTransactionManager tm = this.determineTransactionManager(txAttr);
//获取目标方法的方法名(就是切点)
String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
Object result;
if(){
//省略一部分无关紧要的代码
}else{
//这个很重要,包含有事务和数据库连接等相关信息
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
result = null;
try {
//调用目标方法,访问数据库和业务逻辑处理等
result = invocation.proceedWithInvocation();
} catch (Throwable var17) {
//如果调用目标方法报错了,处理回滚
this.completeTransactionAfterThrowing(txInfo, var17);
throw var17;
} finally {
this.cleanupTransactionInfo(txInfo);
}
//如果调用目标方法没有报错并成功返回,就提交事务
this.commitTransactionAfterReturning(txInfo);
return result;
}
}
只要大家对切面编程aop了解的话,上面的代码就很清晰它的功能了,就是类似于一个环绕通知;
接下来我们就详细了解一下几个具体的方法:
1.createTransactionIfNecessary();创建事务,包含数据库的连接和事务状态等信息;
2.completeTransactionAfterThrowing();调用目标方法报错,回滚;
3.commitTransactionAfterReturning();调用目标方法成功,提交事务(里面有一些判断,不符合条件可能会回滚)
2.1 createTransactionIfNecessary方法
createTransactionIfNecessary()看方法名称就知道,是创建事务,事务包含一些事务本身的信息(传播机制、隔离级别、是否只读、事务的状态、以及数据库连接等)
protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
//省略部分不重要的代码
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
//创建一个TransactionStatus类型的对象,这个很重要,后面详解
status = tm.getTransaction((TransactionDefinition)txAttr);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
}
}
//封装并返回TransactionAspectSupport.TransactionInfo类型的对象,这个包含了事务的所有信息
return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);
}
如上面代码所示,createTransactionIfNecessary方法创建封装了TransactionAspectSupport.TransactionInfo类型的对象,该对象中包含了事务的所有信息;下面我们就来看里面调用的两个方法:
1.tm.getTransaction(),创建一个TransactionStatus类型的对象,里面包含数据库连接信息和事务状态信息;
2.this.this.prepareTransactionInfo(),封装并返回TransactionAspectSupport.TransactionInfo类型的对象,这个包含了事务的所有信息
2.1.2 getTransaction()方法
调用的是AbstractPlatformTransactionManager类getTransaction方法返回transactionStatus;
该方法做的事情比较多,创建数据库连接,维护TransactionSynchronizationManager的线程变量等
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
//调用AbstractPlatformTransactionManager的dogetTransaction方法获取事务datasourceTransactionManager.dataSourceTransactionObject类型的txOBject(里面包含有connectionHolder,只不过第一次为空)
Object transaction = this.doGetTransaction();
//isExistingTransaction方法用于判定上面的transaction是否从TransactionSynchronizationManager的线程变量resources中获取到connectionHolder,如果获取到了为true,表明是共用同一个事务
if (this.isExistingTransaction(transaction)) {
//封装事物,设置该事物不是新的事物,注意,这里的封装对象和第一次进来的事物对象不是同一个对象,只是共用了一个connectionHolder对象罢了,这里会设置该事物对象的newTransaction=false,为后面判断回滚时候需要;
return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
//判断事物的过期时间
} else if (((TransactionDefinition)definition).getTimeout() < -1) {
throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());
//如果当前事务的传播策略是2,也就是PROPAGATION_MANDATORY,中文翻译为强制,支持使用当前事务,如果当前事务不存在,则抛出Exception。
} else if (((TransactionDefinition)definition).getPropagationBehavior() == 2) {
throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
//如果当前事务的传播策略不是0、默认事务,3、REQUIRES_NEW 创建新事务,6、NESTED:嵌套事务,就走下面的方法
} else if (((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) {
if (((TransactionDefinition)definition).getIsolationLevel() != -1 && this.logger.isWarnEnabled()) {
this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = this.getTransactionSynchronization() == 0;
return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization, debugEnabled, (Object)null);
} else {
//我们的传播策略是0,默认策略,走下面的代码
AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = this.suspend((Object)null);
if (debugEnabled) {
this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition);
}
try {
//newSynchronization为true
boolean newSynchronization = this.getTransactionSynchronization() != 2;
//newTransactionStatus创建一个transactionStatus对象,表事务的相关状态
DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//doBegin方法很重要,封装transaction,我们下面详细说明
this.doBegin(transaction, (TransactionDefinition)definition);
//prepareSynchronization是维护TransactionSynchronizationManager的线程变量,TransactionSynchronizationManager很重要,我们下面也会详细说明
this.prepareSynchronization(status, (TransactionDefinition)definition);
return status;
} catch (Error | RuntimeException var7) {
this.resume((Object)null, suspendedResources);
throw var7;
}
}
}
我们重点讲解doGetTransaction方法、doBegin方法、prepareSynchronization方法:
2.1.2.1 doGetTransaction方法
该方法是尝试从TransactionSynchronizationManager的线程变量resources里面获取connectionHolder即数据库连接,第一个方法肯定是null,但是后续被调用方法共用一个事务的话,就可以从resources里面获取相同的connectionHolder;
protected Object doGetTransaction() {
//创建DataSourceTransactionManager的内部类DataSourceTransactionObject对象
DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();
txObject.setSavepointAllowed(this.isNestedTransactionAllowed());
//尝试从TransactionSynchronizationManager类的线程变量resources里面获取connectionHolder,即获取数据库连接,第一次conHolder肯定为null
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
2.1.2.2 doBegin方法
实际调用的DataSourceTransactionManager的doBegin方法,做了下面几件事
1.调用dataSource.getConnection方法获取数据库连接connection
2.将connection封装入ConnectionHolder,并设置synchronizedWithTransaction为true,并将connectionHolder封装到datasourceTransactionManager.dataSourceTransactionObject类型的txOBject对象里面
3.调用dataSourceUtils.prepareConnectionForTransaction方法从注解属性中获取相关值并设置相关属性:
3.1设置数据库连接的事务隔离级别transactionalIsolation,
3.2设置自动提交autoCommit为false,
3.3设置timeout事务过期时间,
3.4设置transactionActive为true
4.将connectionHolder存入TransactionSynchronizationManager的resources,以dataSources为key
下面看源码:
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
Connection con = null;
try {
//第一次肯定没有connectionHolder
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
//第一次肯定需要从数据库连接池中获取数据库连接
Connection newCon = this.obtainDataSource().getConnection();
//将数据库连接封装到ConnectionHolder对象中
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//下面的代码就是一些封装了
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
}
//因为是事务,所以不再自动提交
con.setAutoCommit(false);
}
//该方法无关紧要
this.prepareTransactionalConnection(con, definition);
//表示当前数据库连接用于事务
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = this.determineTimeout(definition);
//设置自定义的事务超时时间
if (timeout != -1) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
//当前的connectionHolder肯定是新的,会走下面的代码
if (txObject.isNewConnectionHolder()) {
//该方法将connectionHolder存入TransactionSynchronizationManager的resources,以dataSources为key,后续被调用方法如果共用一个事务,就会从TransactionSynchronizationManager获取到这个connectionHolder,也就是共用一个连接了
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
}
} catch (Throwable var7) {
//省略部分代码
}
}
综上,doBegin方法获取了数据库连接封装connectionHoler,和事务绑定,并保存在线程变量TransactionSynchronizationManager.resources中;这个步骤很重要,因为后续访问数据库,就会从线程变量中获取connectionHoler,也就获取数据库连接;被调用方法如果是共用一个事务,也会从该connectionHolder中获取连接,即共用一个事务,就会共用一个数据库连接;
上面提到了TransactionSynchronizationManager类,我们这里看一下这个类有什么?
public abstract class TransactionSynchronizationManager {
//存有connectionHolder(当前事务的数据库连接)和sqlSessionHoler(后续访问数据库的会话)
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
//LinkedHashSet 后面访问数据库会存sqlsessionSynchronization对象,属性是sqlsessionFactory和sqlsessionHolder(数据库会话信息)
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
//当前事务的名称,对应@Transaction的transactionManager,没有的话就取方法的全类名
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
//当前事务是否是只读事务 false
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
//当前事务的隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
//是否有事务 true
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");
}
显然这个类里面含有很多线程变量,很多面试官都会问,事务的数据库连接信息是保存在哪里的啊?就是在这个TransactionSynchronizationManager的resources里面;
2.1.2.3 prepareSynchronization方法
如下代码所示,该方法旨在封装TransactionSynchronizationManager的线程变量;具体里面的属性上面已经说明了哈;
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
//isNewSynchronization为true
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(definition.getIsolationLevel() != -1 ? definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
综上:整个tm.getTransaction方法,最重要的就是获取到数据库连接,维护了TransactionSynchronizationManager类里面的线程变量,并将数据库连接同事务绑定,封装一个TransactionStatus对象;
2.1.3 prepareTransactionInfo方法
该方法将得到的数据包括TransactionStatus、DataSourceTransactionManager tm、TransactionAttribute txAttr等封装成TransactionAspectSuport.TransactionInfo对象并返回;
protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) {
//封装TransactionAspectSupport.TransactionInfo txInfo对象
TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
txInfo.newTransactionStatus(status);
} else if (this.logger.isTraceEnabled()) {
//省略无关紧要的代码
}
//该方法的作用暂时不清楚
txInfo.bindToThread();
return txInfo;
}
2.1.4 小节:
整个createTransactionIfNecessary()方法旨在:
1.获取数据库连接封装connectionHolder;
2.维护TransactionSynchronizationManager类里面的线程变量,比如将数据库连接信息存入线程变量resources中;
3.封装TransactionStatus对象;
4.封装TransactionAspectSupport.TransactionInfo并返回;
2.2 调用目标方法
Object var8 = invocation.proceedWithInvocation();
这个就是mybatis的内容了,还是有需要提到的地方就是在调用数据库的时候SpringManagedTransaction类的openConnection方法获取数据库连接
private void openConnection() throws SQLException {
//获取数据库连接
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JDBC Connection [" + this.connection + "] will" + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
}
}
我们具体看一下DataSourceUtils.getConnection方法,里面调用了doGetConnection方法
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
} catch (SQLException var2) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", var2);
} catch (IllegalStateException var3) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + var3.getMessage());
}
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
//从TransactionSynchronizationManager的线程变量resources里面获取connectionHolder,肯定是可以获取到的
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = fetchConnection(dataSource);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
try {
ConnectionHolder holderToUse = conHolder;
if (conHolder == null) {
holderToUse = new ConnectionHolder(con);
} else {
conHolder.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
} catch (RuntimeException var4) {
releaseConnection(con, dataSource);
throw var4;
}
}
return con;
} else {
//我们进入下面的逻辑
//获取到connectionHolder后,将connectionHolder的引用数量referenceCount加1,后续访问完数据库,会将conenctionHolder的引用数量减1
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(fetchConnection(dataSource));
}
//返回数据库连接connection
return conHolder.getConnection();
}
}
看到了没,conHolder.getConnection(),就是从connectionHolder里面获取连接,同一个事务里面,共用connectionHolder,所以同一个事务里面,肯定是共用一个数据库连接的;
2.3 completeTransactionAfterThrowing方法
回到最开始的地方
{
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
result = null;
try {
result = invocation.proceedWithInvocation();
} catch (Throwable var17) {
this.completeTransactionAfterThrowing(txInfo, var17);
throw var17;
} finally {
this.cleanupTransactionInfo(txInfo);
}
this.commitTransactionAfterReturning(txInfo);
return result;
}
如果调用目标方法失败报错,就会调用completeTransactionAfterThrowing方法;
该方法不是单纯的回滚,里面有个地方有点意思;
我们分析一种场景:
@Transactional
public void methodA(){
userMapper.selectById("jzcs");
try {
//methodB会报错,但是这里被try catch了,所以methodA就检查不到错误
infoService.getUserClassInfo("userId");
}catch (Exception e){
e.printStackTrace();
}
}
//methodB是infoService的方法
@Transactional
public void methodB(String userId){
infoMapper.selectUserClassInfo(userId);
//这个地方肯定会报错
System.out.println(1/0);
}
如上所示,在我们的测试案例里面,如果在methodA方法中try catch了methodB方法,如果methodB方法报错了,methodA会不会回滚呢?测试案例里面methodA和methodB是共用的一个事务;
注意:
我们肯定能判定:methodB报错,肯定会走completeTransactionAfterThrowing方法;
methodA没有检查到错误,所以会走commitTransactionAfterReturning方法;
我们带着这个问题去研究代码;
我们看一下methodB走的completeTransactionAfterThrowing方法做了些什么?
protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
//transactionAttribute.rollbackOn(ex)该方法就是判断是否是抛的运行时异常,一般来说肯定是的
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
//执行回滚,最重还是调用的connection.rollback()方法
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} catch (TransactionSystemException var6) {
this.logger.error("Application exception overridden by rollback exception", ex);
var6.initApplicationException(ex);
throw var6;
} catch (Error | RuntimeException var7) {
this.logger.error("Application exception overridden by rollback exception", ex);
throw var7;
}
} else {
//省略部分没有用到的代码
}
}
}
调用AbstractPlatformTransactionManager的rollback方法
public final void rollback(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");
} else {
//调用processRollback方法
DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
this.processRollback(defStatus, false);
}
}
进入到AbstractPlatformTransactionManager的processRollback方法
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
this.triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
this.logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
//如果是第一个方法内部执行报错,它的isNewTransaction为true,就会直接执行doRollback回滚
} else if (status.isNewTransaction()) {
if (status.isDebug()) {
this.logger.debug("Initiating transaction rollback");
}
this.doRollback(status);
} else {
//但是如果是上面说的,是methodB报错,这个是和methodA共用的一个事务,它就会走下面的代码
if (status.hasTransaction()) {
//!isGlobalRollbackOnParticipationFailure为false,!status.isLocalRollbackOnly()为true
if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
//省略部分代码
} else {
//上面说的场景就会走这步
if (status.isDebug()) {
this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
//就会执行这个方法,我们详细看一下该代码
this.doSetRollbackOnly(status);
}
} else {
this.logger.debug("Should roll back transaction but cannot - no transaction available");
}
if (!this.isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
} catch (Error | RuntimeException var8) {
this.triggerAfterCompletion(status, 2);
throw var8;
}
this.triggerAfterCompletion(status, 1);
if (unexpectedRollback) {
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
}
} finally {
this.cleanupAfterCompletion(status);
}
我们详细看一下AbstractPlatformTransactionManager的doSetRollbackOnly这个方法
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
if (status.isDebug()) {
this.logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only");
}
//调用setRollbackOnly方法,我们去看一下具体代码做了什么
txObject.setRollbackOnly();
}
调用DataSourceTransactionManager.DataSourceTransactionObject的setRollbackOnly方法
public void setRollbackOnly() {
this.getConnectionHolder().setRollbackOnly();
}
看到没有,是调用的connectionHolder的setRollbackOnly()方法,connectionHolder这个类大家还记得吗,就是保存到TransactionSynchronizationManager的线程变量resources里面的;里面包含了数据库连接信息;
我们看一下这个方法干了些什么?
public void setRollbackOnly() {
this.rollbackOnly = true;
}
再次先提醒:methoA和methodB共用的一个事务,也就共用的一个connectionHolder哦;
设置connectionHolder的rollbackOnly属性为true;这个标识很重要的,因为methodA的内部,try catch 了methodB方法,如果methodB方法报错了,methodB就会在执行completeTransactionAfterThrowing方法的时候,实际是调用了上面分析的方法,将connectionHolder的rollbackOnly属性设置为true;
好methodB就执行完了,接下来我们看看methodA走的commitTransactionAfterReturning方法;
2.4 commitTransactionAfterReturning方法
如果调用目标方法没有报错,就会调用该方法
protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
//调用commit方法
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
进入AbstractPlatformTransactionManager的commit方法
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");
} else {
//我们进入到这里
DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
//isLocalRollbackOnly为false
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
this.logger.debug("Transactional code has requested rollback");
}
this.processRollback(defStatus, false);
//我们进入到下面方法,!this.shouldCommitOnGlobalRollbackOnly为true,重点是defStatus.isGlobalRollbackOnly()这个方法,我们下面详细看一下这个方法
} else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
this.processRollback(defStatus, true);
} else {
this.processCommit(defStatus);
}
}
}
我们进入到isGlobalRollbackOnly方法:
public boolean isGlobalRollbackOnly() {
//主要是((SmartTransactionObject)this.transaction).isRollbackOnly()这个判断,我们再进入到这个方法
return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly();
}
再进入到isRollbackOnly()方法:
public boolean isRollbackOnly() {
//重点戏来了,获取connectionHolder中的rollbackOnly属性
return this.getConnectionHolder().isRollbackOnly();
}
重点来了:isRollbackOnly()方法,是获取到connectionHolder的rollbackOnly属性,判断是否为true;我们上面的案例里面,methodB报错后,进入到completeTransactionAfterThrowing方法里面,将共用的connectionHolder设置为了true,所以,methodA进入到commitTransactionAfterReturning方法里面发现connectionHolder为true,就会调用processRollback方法执行回滚了;
并且会报错,如下
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
所以,methodA和methodB都会回滚;
当然,如果methodB不报错,肯定就会正常提交啦;
总结:
上面我们介绍了@Transactional注解的源码:
1.TransactionInterceptor负责拦截加了@Transactional注解的方法;
2.TransactionAspectSupport负责对目标方法增强
2.1 在目标方法前创建事务和数据库连接;
2.2 在目标方法报错后执行回滚或者加回滚标识(设置connectionHolder的rollbackOnly为true)
2.3 在目标方法执行成功后执行提交或者回滚(如果检查到connectionHolder的rollbackOnly为true,就会执行回滚,不然就执行提交)