02_Spring事务框架源码

一. 粗略分析@Transactional

假设现在有如下案例。

public class ServiceA {
	@Autowired
	private class ServiceB serviceB;
	
	public void test() {
		serviceB.method();
	}
}

public class ServiceB {
	@Transactional(rollback = Exception.class)
	public void method() {
		...
	}
}

ServiceA通过@Autowired注入的,实际上是一个由Spring根据ServiceB生成的动态代理对象。当ServiceA调用ServiceB的method()方法时,首先动态代理对象会发现,待调用的方法上有一个@Transactional注解,因此会把这个方法当做AOP的切面,在调用method()之前,执行一段前置增强逻辑,目的是开启事务(start transaction)。接着仔细观察method的执行过程,如果发现报错,则回滚当前事务,如果执行正常结束,则在method()方法结束之后,执行一段后置增强逻辑,目的是提交事务(commit)。

整个过程如下图所示
在这里插入图片描述

二. 探究Spring-tx.jar

Spring掌管事务的依赖库是spring-tx.jar,重点org.springframework.transaction.interceptor.TransactionInterceptor。

我们已经知道,调用目标对象的某个方法,实际上这个请求都会被TransactionInteceptor拦截,这个过程看起来就很像JDK动态代理,所以请求一定会被交给拦截器的invoke()方法。

在invoke()方法中,重点是看invokeWithinTransaction(),我们只需要关注这个方法内的核心代码即可,结构非常清晰。

if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
	// [第一步] 创建并开启事务
	TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
	Object retVal = null;
	try {
		// [第二步]执行目标方法的业务逻辑
		retVal = invocation.proceedWithInvocation();
	}
	catch (Throwable ex) {
		// [第四步]如果报错,则"考虑"回滚事务
		completeTransactionAfterThrowing(txInfo, ex);
		throw ex;
	}
	finally {
	    // 一些事务的清理工作
		cleanupTransactionInfo(txInfo);
	}
	// [第三步]能走到这儿,说明上面所有的步骤都没有报错 所以,提交事务
	commitTransactionAfterReturning(txInfo);
	return retVal;
}

所以,接下来就围绕这四步,仔细的分析源码。

2.1 创建并开启事务

  1. spring-tx.jar TransactionAspectSupport createTransactionIfNecessary( )
    核心代码: PlatformTransactionManager #getTransaction()
  2. spring-tx.jar AbstractPlatformTransactionManager getTransaction()
    核心代码1: Object transaction = doGetTransaction();
    核心代码2: doBegin(transaction, definition);
  3. spring-orm.jar JpaTransactionManager doGetTransaction()
    获取一个JpaTransactionObject对象。
  4. spring-orm.jar JpaTransactionManager doBegin()
    核心代码: getJpaDialect().beginTransaction()
    这里的getJpaDialect()获取到的是来自hibernate的HibernateJpaDialect,到这里为止,我们可以看到,spring事务的底层用hibernate扯上了关系。
  5. spring-orm.jar HibernateJpaDialect beginTransaction()
    核心代码: entityManager.getTransaction().begin();
  6. hibernate-entitymanager.jar TransactionImpl begin()
    核心代码: Transaction tx = getSession().beginTransaction();
  7. hibernate-core.jar beginTransaction()
  8. hibernate-core.jar AbstractLogicalConnectionImplementor begin()
    第七步到第八步之间,跳了一些步骤,因为已经偏底层了,层数太多,代码全部罗列出来没有意义。我们只需要关注有用的代码即可。
	@Override
	public void begin() {
		try {
			log.trace( "Preparing to begin transaction via JDBC Connection.setAutoCommit(false)" );
			getConnectionForTransactionManagement().setAutoCommit( false );
			status = TransactionStatus.ACTIVE;
			log.trace( "Transaction begun via JDBC Connection.setAutoCommit(false)" );
		}
		catch( SQLException e ) {
			throw new TransactionException( "JDBC begin transaction failed: ", e );
		}
	}

上述代码中,getConnectionForTransactionManagement()返回的是java.sql.Connection,所以我们把这段代码翻译一下:

从事先准备好的数据库连接池中,获取一条已经与Mysql建立通道的连接(Connection),并且,设置成"关闭自动提交",与此同时,开启事务。底层靠的是Mysql驱动发送了相应的指令给Mysql。

2.2 执行目标方法的业务逻辑

既然已经开启了事务,那么接下来就需要执行目标业务逻辑了,执行目标方法的代码是

spring-tx.jar TransactionAspectSupport invocation.proceedWithInvocation()

其实就是通过反射,调用被代理的类的目标方法。

2.3 提交事务

如果业务逻辑没有报错,那么就提交事务。

  1. spring-tx.jar TransactionAspectSupport invokeWithinTransaction()
    这是执行事务处理的主入口。
  2. spring-tx.jar TransactionAspectSupport commitTransactionAfterReturning(txInfo);
    当开启事务、执行业务逻辑等步骤全部执行完毕,且没有报错,则开始着手提交事务。
  3. spring-tx.jar TransactionAspectSupport commitTransactionAfterReturning()
    核心方法: txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
  4. spring-tx.jar AbstractPlatformTransactionManager commit()
    核心方法: processCommit(defStatus);
  5. spring-orm.jar JpaTransactionManager doCommit()
    开始执行JpaTransactionManager commit()
  6. hibernate-core.jar AbstractLogicalConnectionImplementor commit()
    从第五步到第六步省略了一些步骤,
@Override
public void commit() {
	try {
		log.trace( "Preparing to commit transaction via JDBC Connection.commit()" );
		getConnectionForTransactionManagement().commit();
		status = TransactionStatus.COMMITTED;
		log.trace( "Transaction committed via JDBC Connection.commit()" );
	}
	catch( SQLException e ) {
		status = TransactionStatus.FAILED_COMMIT;
		throw new TransactionException( "Unable to commit against JDBC Connection", e );
	}

	afterCompletion();
}

核心代码是: getConnectionForTransactionManagement().commit();
其实就是通过java.sql.Connection #commit() 提交了事务。

2.4 事务回滚

如果执行目标方法的业务逻辑时报错,根据invokeWithinTransaction()的代码,我们知道,错误一定会被catch住,接着执行
completeTransactionAfterThrowing(txInfo, ex); 回滚事务。

整个回滚的过程与提交事务的过程基本相同,首先通过Spring的TransactionManager处理,接着找到hibernate的entitymanager来处理,最后底层仍然是基于JDBC Connection的rollback()方法完成事务回滚。

摘自AbstractLogicalConnectionImplementor.java

@Override
public void rollback() {
	try {
		log.trace( "Preparing to rollback transaction via JDBC Connection.rollback()" );
		getConnectionForTransactionManagement().rollback();
		status = TransactionStatus.ROLLED_BACK;
		log.trace( "Transaction rolled-back via JDBC Connection.rollback()" );
	}
	catch( SQLException e ) {
		throw new TransactionException( "Unable to rollback against JDBC Connection", e );
	}

	afterCompletion();
}

2.5 总结

Spring的事务机制非常简单,可以用一段话来概括: 一个类在调用目标对象的指定方法时,实际上调用的是由Spring提供的动态代理对象,由于发现目标对象被@Transactional注解标注,所以利用AOP的思想,为动态代理对象的目标方法插入了一段增强逻辑(显然就是一个环绕增强),所以请求会被TransactionInterceptor拦截,并执行invoke()方法。首先,借助JDBC Connection开启事务,默认事务不自动提交,接着,通过反射执行实际对象的指定方法,整个业务逻辑的执行过程全部笼罩在事务之中,如果执行报错,则借助JDBC Connection的rollback()回滚事务,否则就借助JDBC Connection的commit()提交事务,提交事务后,对数据库的相关操作就真正的体现在了数据库当中。

上面那些代码细节看起来比较繁琐,其实无非就是Spring的事务管理器与Hibernate的代码进行交互,由Hibernate去找JDBC Connection完成最终的操作,比如开启事务、事务回滚、事务提交。这么多的代码,其实核心只有一个方法:

摘自TransactionAspectSupport invokeWithinTransaction(),逻辑非常清晰

		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			Object retVal = null;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

把上述内容画成一张图,可以放大查看
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值