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

推荐学习

  • 肝了十天半月 , 献上纯手绘“Spring/Cloud/Boot/MVC”全家桶脑图
  • 疯狂膜拜!阿里出品Spring Security王者晋级文档
  • 真香警告!Alibaba珍藏版mybatis手写文档 , 刷起来

面试官:问你一个,Spring事务是如何传播的?文章插图
前言Spring事务是如何传播的?
其实之前有分析事务注解的解析过程 , 本质上是将事务封装为切面加入到AOP的执行链中 , 因此会调用到MethodInceptor的实现类的invoke方法 , 而事务切面的Interceptor就是TransactionInterceptor , 所以本篇直接从该类开始 。
事务切面的调用过程 public Object invoke(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, invocation::proceed); }这个方法本身没做什么事 , 主要是调用了父类的invokeWithinTransaction方法 , 注意最后一个参数 , 传入的是一个lambda表达式 , 而这个表达式中的调用的方法应该不陌生 , 在分析AOP调用链时 , 就是通过这个方法传递到下一个切面或是调用被代理实例的方法 , 忘记了的可以回去看看 。
protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass,final InvocationCallback invocation) throws Throwable {// If the transaction attribute is null, the method is non-transactional.//获取事务属性类 AnnotationTransactionAttributeSourceTransactionAttributeSource tas = getTransactionAttributeSource();//获取方法上面有@Transactional注解的属性final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);//获取事务管理器final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 调用proceed方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception//事务回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}//事务提交commitTransactionAfterReturning(txInfo);return retVal;}// 省略了else }这个方法逻辑很清晰 , 一目了然 , if里面就是对声明式事务的处理 , 先调用createTransactionIfNecessary方法开启事务 , 然后通过invocation.proceedWithInvocation调用下一个切面 , 如果没有其它切面了 , 就是调用被代理类的方法 , 出现异常就回滚 , 否则提交事务 , 这就是Spring事务切面的执行过程 。 但是 , 我们主要要搞懂的就是在这些方法中是如何管理事务以及事务在多个方法之间是如何传播的 。
事务的传播性概念传播性是Spring自己搞出来的 , 数据库是没有的 , 因为涉及到方法间的调用 , 那么必然就需要考虑事务在这些方法之间如何流转 , 所以Spring提供了7个传播属性供选择 , 可以将其看成两大类 , 即是否支持当前事务:
  1. 支持当前事务(在同一个事务中):
PROPAGATION_REQUIRED:支持当前事务 , 如果不存在 , 就新建一个事务 。 PROPAGATION_MANDATORY:支持当前事务 , 如果不存在 , 就抛出异常 。 PROPAGATION_SUPPORTS:支持当前事务 , 如果不存在 , 就不使用事务 。
  1. 不支持当前事务(不在同一个事务中):
PROPAGATION_NEVER:以非事务的方式运行 , 如果有事务 , 则抛出异常 。 PROPAGATION_NOT_SUPPORTED:以非事务的方式运行 , 如果有事务 , 则挂起当前事务 。 PROPAGATION_REQUIRES_NEW:新建事务 , 如果有事务 , 挂起当前事务(两个事务相互独立 , 父事务回滚不影响子事务) 。 PROPAGATION_NESTED:如果当前事务存在 , 则嵌套事务执行(指必须依存父事务 , 子事务不能单独提交且父事务回滚则子事务也必须回滚 , 而子事务若回滚 , 父事务可以回滚也可以捕获异常) 。 如果当前没有事务 , 则进行与PROPAGATION_REQUIRED类似的操作 。
别看属性这么多 , 实际上我们主要用的是PROPAGATION_REQUIRED默认属性 , 一些特殊业务下可能会用到PROPAGATION_REQUIRES_NEW以及PROPAGATION_NESTED 。 下面我会假设一个场景 , 并主要分析这三个属性 。
public class A {@Autowired private B b; @Transactional public void addA() {b.addB(); }}public class B { @Transactional public void addB() {// doSomething... }}