3.3 事务执行
回到业务逻辑,通过 louzai 的 AOP 代理对象,开始执行主方法。
因为代理对象是 Cglib 方式创建,所以通过 Cglib 来执行。
这里是重点!敲黑板!!!
下面的代码是事务执行的核心逻辑 invokeWithinTransaction()。
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { //获取我们的事务属源对象 TransactionAttributeSource tas = getTransactionAttributeSource(); //通过事务属性源对象获取到我们的事务属性信息 final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); //获取我们配置的事务管理器对象 final PlatformTransactionManager tm = determineTransactionManager(txAttr); //从tx属性对象中获取出标注了@Transactionl的方法描述符 final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); //处理声明式事务 if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { //有没有必要创建事务 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal; try { //调用钩子函数进行回调目标方法 retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { //抛出异常进行回滚处理 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { //清空我们的线程变量中transactionInfo的值 cleanupTransactionInfo(txInfo); } //提交事务 commitTransactionAfterReturning(txInfo); return retVal; } //编程式事务 else { // 这里不是我们的重点,省略... } }
3.3.1 获取事务属性
在 invokeWithinTransaction() 中,我们找到获取事务属性的入口。
从 attributeCache 获取事务的缓存数据,缓存数据是在 “2.2.1 获取切面列表” 中保存的。
3.3.2 创建事务
通过 doGetTransaction() 获取事务。
protected Object doGetTransaction() { //创建一个数据源事务对象 DataSourceTransactionObject txObject = new DataSourceTransactionObject(); //是否允许当前事务设置保持点 txObject.setSavepointAllowed(isNestedTransactionAllowed()); /** * TransactionSynchronizationManager 事务同步管理器对象(该类中都是局部线程变量) * 用来保存当前事务的信息,我们第一次从这里去线程变量中获取 事务连接持有器对象 通过数据源为key去获取 * 由于第一次进来开始事务 我们的事务同步管理器中没有被存放.所以此时获取出来的conHolder为null */ ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder, false); //返回事务对象 return txObject; }
通过 startTransaction() 开启事务。
下面是开启事务的详细逻辑,了解一下即可。
protected void doBegin(Object transaction, TransactionDefinition definition) { //强制转化事务对象 DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { //判断事务对象没有数据库连接持有器 if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { //通过数据源获取一个数据库连接对象 Connection newCon = obtainDataSource().getConnection(); if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } //把我们的数据库连接包装成一个ConnectionHolder对象 然后设置到我们的txObject对象中去 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); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); } //判断事务为只读事务 prepareTransactionalConnection(con, definition); //设置事务激活 txObject.getConnectionHolder().setTransactionActive(true); //设置事务超时时间 int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // 绑定我们的数据源和连接到我们的同步管理器上 把数据源作为key,数据库连接作为value 设置到线程变量中 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { //释放数据库连接 DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
最后返回到 invokeWithinTransaction(),得到 txInfo 对象。
3.3.3 执行逻辑
还是在 invokeWithinTransaction() 中,开始执行业务逻辑。
进入到真正的业务逻辑。
执行完毕后抛出异常,依次返回,走后续的回滚事务逻辑。
3.3.4 回滚事务
还是在 invokeWithinTransaction() 中,进入回滚事务的逻辑。
。
执行回滚逻辑很简单,我们只看如何判断是否回滚。
如果抛出的异常类型,和事务定义的异常类型匹配,证明该异常需要捕获。
之所以用递归,不仅需要判断抛出异常的本身,还需要判断它继承的父类异常,满足任意一个即可捕获。
到这里,所有的流程结束。
4. 结语
我们再小节一下,文章先介绍了事务的使用示例,以及事务的执行流程。
之后再剖析了事务的源码,分为 2 块:
- 先匹配出 louzai 对象所有关于事务的切面列表,并将匹配成功的事务属性保存到缓存;
- 从缓存取出事务属性,然后创建、启动事务,执行业务逻辑,最后提交或者回滚事务。
这篇文章,是 Spring 源码解析的第 4 篇,如果之前已经看过 AOP 的源码解析,这篇就要容易很多,但是如果上来就直接肝,可能会有那么一丢丢难度。
源码系列共有 5 篇,你猜猜最后一篇,我会写什么呢,可以给楼仔留言哈。
这篇文章是我周末写的,从白天到黑夜,肝了我整整 2 天,原创不易,大家的点赞和分享,是我继续创作的最大动力!
























