面试官:问你一个,Spring事务是如何传播的?( 三 )

这个方法流程比较长 , 一步步来看 , 先调用doGetTransaction方法获取一个DataSourceTransactionObject对象 , 这个类是JdbcTransactionObjectSupport的子类 , 在父类中持有了一个ConnectionHolder对象 , 见名知意 , 这个对象保存了当前的连接 。
protected Object doGetTransaction() {//管理connection对象 , 创建回滚点 , 按照回滚点回滚 , 释放回滚点DataSourceTransactionObject txObject = new DataSourceTransactionObject();//DataSourceTransactionManager默认是允许嵌套事务的txObject.setSavepointAllowed(isNestedTransactionAllowed());//obtainDataSource() 获取数据源对象 , 其实就是数据库连接块对象ConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());txObject.setConnectionHolder(conHolder, false);return txObject; }追溯getResource方法可以看到ConnectionHolder 是从ThreadLocal里获取的 , 也就是当前线程 , key是DataSource对象;但是仔细思考下我们是第一次进来 , 所以这里肯定获取不到的 , 反之 , 要从这里获取到值 , 那必然是同一个线程第二次及以后进入到这里 , 也就是在addA调用addB时 , 另外需要注意这里保存ConnectionHolder到DataSourceTransactionObject对象时是将newConnectionHolder属性设置为false了的 。 继续往后 , 创建完transaction对象后 , 会调用isExistingTransaction判断是否已经存在一个事务 , 如果存在就会调用handleExistingTransaction方法 , 这个方法就是处理事务传播的核心方法 , 因为我们是第一次进来 , 肯定不存在事务 , 所以先跳过 。 再往后 , 可以看到就是处理不同的传播属性 , 主要看到下面这个部分:
【面试官:问你一个,Spring事务是如何传播的?】else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {//先挂起SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);//创建事务状态对象 , 其实就是封装了事务对象的一些信息 , 记录事务状态的DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);//开启事务,重点看看 DataSourceTransactionObjectdoBegin(transaction, definition);//开启事务后 , 改变事务状态prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}第一次进来时 , PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED都会进入到这里 , 首先会调用suspend挂起当前存在的事务 , 在这里没啥作用 。 接下来通过newTransactionStatus创建了DefaultTransactionStatus对象 , 这个对象主要就是存储当前事务的一些状态信息 , 需要特别注意newTransaction属性设置为了true , 表示是一个新事务 。 状态对象创建好之后就是通过doBegin开启事务 , 这是一个模板方法:
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.setConnectionHolder(new ConnectionHolder(newCon), true);}txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();//从数据库连接中获取隔离级别Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,// so we don't want to do it unnecessarily (for example if we've explicitly// configured the connection pool to set it already).if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}//关闭连接的自动提交 , 其实这步就是开启了事务con.setAutoCommit(false);}//设置只读事务 从这一点设置的时间点开始(时间点a)到这个事务结束的过程中 , 其他事务所提交的数据 , 该事务将看不见!//设置只读事务就是告诉数据库 , 我这个事务内没有新增 , 修改 , 删除操作只有查询操作 , 不需要数据库锁等操作 , 减少数据库压力prepareTransactionalConnection(con, definition);//自动提交关闭了 , 就说明已经开启事务了 , 事务是活动的txObject.getConnectionHolder().setTransactionActive(true);int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// Bind the connection holder to the thread.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);} }


推荐阅读