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 创建并开启事务
- spring-tx.jar TransactionAspectSupport createTransactionIfNecessary( )
核心代码: PlatformTransactionManager #getTransaction() - spring-tx.jar AbstractPlatformTransactionManager getTransaction()
核心代码1: Object transaction = doGetTransaction();
核心代码2: doBegin(transaction, definition); - spring-orm.jar JpaTransactionManager doGetTransaction()
获取一个JpaTransactionObject对象。 - spring-orm.jar JpaTransactionManager doBegin()
核心代码: getJpaDialect().beginTransaction()
这里的getJpaDialect()获取到的是来自hibernate的HibernateJpaDialect,到这里为止,我们可以看到,spring事务的底层用hibernate扯上了关系。 - spring-orm.jar HibernateJpaDialect beginTransaction()
核心代码: entityManager.getTransaction().begin(); - hibernate-entitymanager.jar TransactionImpl begin()
核心代码: Transaction tx = getSession().beginTransaction(); - hibernate-core.jar beginTransaction()
- 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 提交事务
如果业务逻辑没有报错,那么就提交事务。
- spring-tx.jar TransactionAspectSupport invokeWithinTransaction()
这是执行事务处理的主入口。 - spring-tx.jar TransactionAspectSupport commitTransactionAfterReturning(txInfo);
当开启事务、执行业务逻辑等步骤全部执行完毕,且没有报错,则开始着手提交事务。 - spring-tx.jar TransactionAspectSupport commitTransactionAfterReturning()
核心方法: txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); - spring-tx.jar AbstractPlatformTransactionManager commit()
核心方法: processCommit(defStatus); - spring-orm.jar JpaTransactionManager doCommit()
开始执行JpaTransactionManager commit() - 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;
}
把上述内容画成一张图,可以放大查看