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

上面我创建了A、B两个类 , 每个类中有一个事务方法 , 使用了声明式事务并采用的默认传播属性 , 在A中调用了B的方法 。 当请求来了调用addA时 , 首先调用的是代理对象的方法 , 因此会进入createTransactionIfNecessary方法开启事务:
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {// If no name specified, apply method identification as transaction name.if (txAttr != null}};}TransactionStatus status = null;if (txAttr != null) {if (tm != null) {//开启事务 , 这里重点看status = tm.getTransaction(txAttr);}else {}}//创建事务信息对象 , 记录新老事务信息对象return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); }实际上开启事务是通过AbstractPlatformTransactionManager做的 , 而这个类是一个抽象类 , 具体实例化的对象就是我们在项目里常配置的DataSourceTransactionManager对象 。
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {//这里重点看 , .DataSourceTransactionObject拿到对象Object transaction = doGetTransaction();// Cache debug flag to avoid repeated checks.boolean debugEnabled = logger.isDebugEnabled();if (definition == null) {// Use defaults if no transaction definition given.definition = new DefaultTransactionDefinition();}//第一次进来connectionHolder为空的 , 所以不存在事务if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(definition, transaction, debugEnabled);}// Check definition settings for new transaction.if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}// No existing transaction found -> check propagation behavior to find out how to proceed.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}//第一次进来大部分会走这里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;}}else {boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);} }这个方法流程比较长 , 一步步来看 , 先调用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方法 , 这个方法就是处理事务传播的核心方法 , 因为我们是第一次进来 , 肯定不存在事务 , 所以先跳过 。 再往后 , 可以看到就是处理不同的传播属性 , 主要看到下面这个部分: