今天又研究了一道面试题,有关 @Transactional 注解失效的问题,这道题的综合性较强。


先说下我整理的答案:


1、@Transactional 作用在非 public 修饰的方法上


2、@Transactional 作用于接口,使用 CGLib 动态代理


3、@Transactional 注解属性 propagation 设置以下三种可能导致无法回滚

  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。


4、同一类中加 @Transactional 方法被无 @Transactional 的方法调用,事务失效


5、@Transactional 方法内异常被捕获


6、默认 RuntimeException 和 Error 及子类抛出,会回滚;rollbackFor 指定的异常及子类发生才会回滚


7、数据库不支持事务,如 MySLQL 使用 MyISAM 存储引擎


8、Spring 的配置文件中未配置事务注解生效

<tx:annotation-driven transaction-manager="transactionManager"/>


9、Spring Boot 引入 jbdc 或 jpa 包,默认开启事务注解。若未引入这两个包,需要使用 @EnableTransactionManagement 进行配置

...


题目与答案已汇总于 小程序


简单分析一下:

@Transactional 注解的作用就是保证方法内的多个数据库操作具有事务特性,即要么都成功,要么都失败。

失效的本质原因就在注解启用、事务管理器注入、手动 commit 与 rollback 的处理(Connection 的 autoCommit 设置为 false,然后在代码里手动 commit 或 rollback)这三个环节中。


比如我的博客网站 xml 配置文件中配置的事务管理器就是

org.springframework.jdbc.datasource.DataSourceTransactionManager

它继承自

org.springframework.transaction.support.AbstractPlatformTransactionManager

事务的具体控制就是 DataSourceTransactionManager 实现。


Spring 启动时解析 xml 配置,把 TransactionInterceptor 类型的 bean 注入到了 BeanFactoryTransactionAttributeSourceAdvisor 中,调用 TransactionInterceptor 的 invoke 方法完成整个事务的逻辑。

@Override  public Object invoke(final MethodInvocation invocation) throws Throwable {    // Work out the target class: may be {@code null}.    // The TransactionAttributeSource should be passed the target class    // as well as the method, which may be from an interface.    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
   // Adapt to TransactionAspectSupport's invokeWithinTransaction...    return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {      @Override      public Object proceedWithInvocation() throws Throwable {        return invocation.proceed();      }    });  }


invokeWithinTransaction 方法就是对事务的处理。


@Transactional 仅作用于 public 的逻辑在 getTransactionAttribute方法中

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {    // Don't allow no-public methods as required.    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {      return null;    }


@Transactional 的注解参数解析逻辑在 invokeWithinTransaction 方法

//If the transaction attribute is null, the method is non-transactional.    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
// First try is the method in the target class.    TransactionAttribute txAtt = findTransactionAttribute(specificMethod);    if (txAtt != null) {      return txAtt;    }
   // Second try is the transaction attribute on the target class.    txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());    if (txAtt != null) {      return txAtt;    }
   if (specificMethod != method) {      // Fallback is to look at the original method.      txAtt = findTransactionAttribute(method);      if (txAtt != null) {        return txAtt;      }      // Last fallback is the class of the original method.      return findTransactionAttribute(method.getDeclaringClass());    }


事务信息通过 createTransactionIfNecessary 方法创建

// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);


代码执行发生异常的处理逻辑是在 completeTransactionAfterThrowing 方法中

completeTransactionAfterThrowing(txInfo, ex);


根据注解参数 rollback

if (txInfo.transactionAttribute.rollbackOn(ex)) {        try {          txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());        }        catch (TransactionSystemException ex2) {          logger.error("Application exception overridden by rollback exception", ex);          ex2.initApplicationException(ex);          throw ex2;        }        catch (RuntimeException ex2) {          logger.error("Application exception overridden by rollback exception", ex);          throw ex2;        }        catch (Error err) {          logger.error("Application exception overridden by rollback error", ex);          throw err;        }      }


根据注解参数 commit

else {        // We don't roll back on this exception.        // Will still roll back if TransactionStatus.isRollbackOnly() is true.        try {          txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());        }        catch (TransactionSystemException ex2) {          logger.error("Application exception overridden by commit exception", ex);          ex2.initApplicationException(ex);          throw ex2;        }        catch (RuntimeException ex2) {          logger.error("Application exception overridden by commit exception", ex);          throw ex2;        }        catch (Error err) {          logger.error("Application exception overridden by commit error", ex);          throw err;        }      }    }


代码执行正常的事务提交在 commitTransactionAfterReturning 方法

commitTransactionAfterReturning(txInfo);


以上就是 Spring 对事务的处理流程。


更多相关文章

  1. 注解就这么简单
  2. Springboot整合mybatis多数据源(注解完整版)
  3. Springboot整合mybatis(注解而且能看明白版本)
  4. mysql从入门到优化(3)事务的基本操作
  5. 分布式系统的事务处理
  6. 扒一扒 @SpringBootApplication 注解背后的奥秘!
  7. 一文讲透微服务下如何保证事务的一致性
  8. 聊聊java中的注解

随机推荐

  1. 对APK进行解包和二次打包(Android)
  2. 数据未显示在Listview中
  3. Android 属性动画(Property Animation)
  4. ADT下搭建JNI编译环境
  5. Android 待机功能流程分析
  6. HTC One X的S720e采用了Beats Audio锁定
  7. Java已经启动,但是返回的退出代码=13 ecli
  8. 使用 gradle 编译多版本 android 应用
  9. Android 外部唤起应用跳转指定页面
  10. 封面流:做无限循环(像启动器,当到达最后一项