上篇博客解析了事务生成动态代理对象的底层代码,简单总结的话就是一句话:如果添加了事务注解@Transactional,且方法是public的,spring就会给该bean生成代理对象,至于是jdk还是cglib,就看自己的事务方法对应的类是否实现了接口
接下来我们来看生成代理对象之后的执行过程

拦截器执行

org.springframework.transaction.interceptor.TransactionInterceptor

我在学习的时候,用的是cglib代理,因为我没有实现接口,所以使用的是CGLIB代理,在目标方法被调用的的时候,是会调用到

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept; 然后会调用到org.springframework.transaction.interceptor.TransactionInterceptor#invokeorg.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

这个方法我删减了一部分,区分了声明式事务和编程式事务,这里我们只关注声明式事务

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// If the transaction attribute is null, the method is non-transactional.TransactionAttributeSource tas = getTransactionAttributeSource();//获取事务属性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)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.//看是否有必要创建一个事务,根据事务的传播行为做判断TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.//执行目标方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception//如果有异常就回滚事务,回滚时,根据配置的rollbackFor进行判断completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清除事务信息cleanupTransactionInfo(txInfo);}//提交事务commitTransactionAfterReturning(txInfo);return retVal;}
}

这个方法中,

  • 根据事务注解对应的传播机制,判断是否要创建新的事务,还是嵌套在当前事务中运行等;
  • 接着执行目标方法
  • 在执行完目标方法之后,如果未发生异常,则提交事务
  • 如果发生异常,根据配置的rollbackFor和noRollbackFor等判断是否要回滚,如果无需回滚,就提交事务,反之,则回滚事务

因此,我们着重关注两个方法

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
和
completeTransactionAfterThrowing(txInfo, ex);

传播机制源码

传播机制的源码我整理之后再补充

异常回滚源码

如果在执行目标方法的时候,如果发生异常,捕获到之后,会进入到这个方法来处理

/*** 这里是判断是否需要回滚的逻辑* 如果在事务注解上指定了回滚的异常类型、或者指定了不回滚的异常类型,就会在这里进行判断* 1.如果判断满足回滚的条件,就事务回滚*      先判断开发人员指定的类型,如果业务代码抛出的异常符合指定的类型,就回滚*      如果没有指定,就判断是否是错误(error)或者运行时异常(runTimeException),如果是,就回滚*    所以:如果程序员没有指定回滚的异常,默认情况下,如果是运行时异常或者是error(错误),也是会进行事务的回滚* 2.否则,就提交事务* @param txInfo* @param ex*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +"] after exception: " + ex);}/*** 如果程序员有指定回滚或者不回滚的异常,就会进入*     org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn(java.lang.Throwable)进行判断** 如果没有指定,就默认调用org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(java.lang.Throwable)* 进行判断  */if (txInfo.transactionAttribute != null && 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 | Error ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}}else {// 这里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 | Error ex2) {logger.error("Application exception overridden by commit exception", ex);throw ex2;}}}
}

这里比较重要的一个方法,就是进行异常的判断,判断当前业务代码抛出的异常,是否符合回滚的要求;

这里这个方法就是来判断当前抛出的异常是否符合在@Transactional注解上配置的异常回滚信息

org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn@Overridepublic boolean rollbackOn(Throwable ex) {if (logger.isTraceEnabled()) {logger.trace("Applying rules to determine whether transaction should rollback on " + ex);}RollbackRuleAttribute winner = null;int deepest = Integer.MAX_VALUE;// 这里的rollbackRules是在解析@Transactional注解的时候,保存的;parseTransactionAnnotation在这个方法中解析的注解;这里的rollbackRules既有rollbackException中配置的,也有noRollbackException中配置的if (this.rollbackRules != null) {for (RollbackRuleAttribute rule : this.rollbackRules) {/*** depth:当前rule和ex的相似度** deepest:和ex最相近的depth* winner:相似度最近的RollbackRuleAttribute* 如果当前rule,返回的depth比上一次返回的depth小,且大于0,就用当前这次异常** 这里会保存和业务代码抛出异常最相似的rule* 比如:*     我在代码中,抛出了一个java.lang.ArithmeticException: / by zero**    如果我在@Transactional(rollbackFor = {Exception.class,ArithmeticException.class})*    那肯定会返回ArithmeticException这个rule,因为ArithmeticException返回的depth是0*     Exception返回的的depth是2  下面解释为什么一个是0,一个是2*/int depth = rule.getDepth(ex);if (depth >= 0 && depth < deepest) {deepest = depth;winner = rule;}}}if (logger.isTraceEnabled()) {logger.trace("Winning rollback rule is: " + winner);}// User superclass behavior (rollback on unchecked) if no rule matches.// 如果没有匹配到最相似的异常、或者没有配置回滚异常类,就会执行这里,这里是调用父类的方法进行判断if (winner == null) {logger.trace("No relevant rollback rule found: applying default rules");return super.rollbackOn(ex);}/*** 如果相似度最近的rule不是无需回滚的类型,就可以进行事务回滚*/return !(winner instanceof NoRollbackRuleAttribute);}
 // 这里就是父类对应的判断逻辑,也就是说:如果程序员单单加了一个@Transactional注解,那么在业务代码抛出运行时异常后者error的时候,还是会回滚的@Overridepublic boolean rollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error);}

那我们接着来看上面判断异常相似度的方法:int depth = rule.getDepth(ex);

这里我称之为相似度,不知道是否合适;这里是根据业务代码中抛出的异常A对应的name和程序员指定的异常B对应的name进行比较,如果匹配上,就返回depth,如果匹配不上,就递归调用,用业务代码中抛出的A异常对应的父类和B异常的name进行比较,依次递归调用

/*** 这里其实就是用业务代码抛出的异常和程序员在@Transactional注解中执行的异常信息进行对比** 实际底层使用的是String.contains()方法* exceptionName:就是程序员指定的异常对应的类名;比如:我指定的是rollbackFor = Exception.class,那这里的exceptionName就是:java.lang.Exception** 1.如果当前抛出的异常和程序员指定的异常匹配不上,就依次递归调用抛出异常的父类和程序员指定的异常进行比较,*        1.1 直到匹配上,就返回当前的depth,depth每递归调用一次,就+1*       1.2 或者是到Throwable依旧没有比对上,这时,就表示我指定的异常和代码抛出的异常不匹配**    这两种场景也好验证:*      1.首先,我在业务代码中,加上这么一行代码:int i = 10/0;*      2.然后在@Transactional注解中加上rollbackException = Exception.class   或者是rollbackException = IoException.class*      这两种异常,最后事务都会回滚,但是效果却是不一样的*        如果我加的是rollbackException = Exception.class,这里会匹配上,返回的是depth是2*        但是如果加的是rollbackException = IoException.class,这里返回的是-1**         因为:如果是rollbackException = Exception.class;那这里在匹配的时候,会递归调用两次,*      int i = 10/0;会抛出java.lang.ArithmeticException: / by zero*      ArithmeticException的父类是RuntimeException;RuntimeException的父类是Exception;所以只有递归调用两次,才能匹配到我指定的Exception.class**        但是,如果我指定的是IoException.class,那永远也匹配不上,因为IOException和ArithmeticException都继承了RuntimeException,是并行的关系,在最后*       递归调用到父类Throwable的时候,就会返回-1(即使这里返回了-1,最后事务还是会回滚,为什么?因为在方法之后会判断,如果程序员指定的异常和当前业务代码抛出的异常不相似,那就会判断业务代码抛出的异常是否是运行时异常或者error)** @param exceptionClass:当前业务代码抛出的异常/或者是抛出异常对应的父类* @param depth:相似度/或者说是深度* @return*/private int getDepth(Class<?> exceptionClass, int depth) {if (exceptionClass.getName().contains(this.exceptionName)) {// Found it!return depth;}// If we've gone as far as we can go and haven't found it...if (exceptionClass == Throwable.class) {return -1;}return getDepth(exceptionClass.getSuperclass(), depth + 1);}

所以,在业务代码抛出异常的时候,

  1. 会判断异常的类型和程序员指定的异常类型,会找到匹配度最为相似的异常,最后再判断最相似的异常 是否是noRollbackException配置的异常类,如果是,就无需回滚,提交事务即可
  2. 如果没有匹配上,就判断抛出的异常是否是Error或者RuntimeException,如果是,也会回滚

spring事务源码执行过程分析相关推荐

  1. Spring源码分析-Spring事务源码分析

    导语      在配置Spring事务管理的时候会用到一个类TransactionInterceptor,从下面的类关系图中可以看到TransactionInterceptor继承了MethodInt ...

  2. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  3. Spring事务源码分析责任链事务链事务不生效

    文章目录 前言 带着问题分析源码 事务源码分析 寻找Spring事务源码类 TransactionInterceptor调用栈 分析Spring AOP责任链 分析TransactionInterce ...

  4. Spring事务源码(一)

    Spring事务源码(一) 前言 一.ProxyTransactionManagementConfiguration 二.BeanFactoryTransactionAttributeSourceAd ...

  5. Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析

      基于最新Spring 5.x,详细介绍了Spring 事务源码,包括< tx:annotation-driven/>标签源码解析.   此前我们已经学习了Spring的<tx:a ...

  6. Spring事务源码详解

    一. 简介 事务: 事务是逻辑上的一组操作,要么都执行,要么都不执行,关于事务的基本知识可以看我的这篇文章:事务的基础知识 Spring事务: Spring 支持两种方式的事务管理:编程式事务管理.声 ...

  7. 【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  8. 足够应付面试的Spring事务源码阅读梳理

    来源:编程新说 Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答 ...

  9. 事务注解放到类上面 下面私有方法有效吗_【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)...

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  10. Spring事务源码分析

    首先看例子,这例子摘抄自开涛的跟我学spring3. @Test public void testPlatformTransactionManager() { DefaultTransactionDe ...

最新文章

  1. 虚拟机复制后修改eth1为eth0
  2. matlab 多目标规划
  3. FreeMarker template error: The following has evaluated to null or missing
  4. android 新闻编辑,超机访问:ZOL手机新闻编辑背后的故事
  5. java常用的排序算法的思想以及实现
  6. 0-2岁的app开发人员必读,Android开发APP前的准备事项
  7. 线段树初见——区间询问与改变最大值
  8. 摆脱困境:将属性值注入配置Bean
  9. java足球游戏毕业设计,java毕业设计_springboot框架的校园足球管理平台
  10. 剑指Offer - 面试题38. 字符串的排列(全排列,排序,回溯+剪枝)
  11. terminal登录mysql_转载-MySQL之终端(Terminal)管理MySQL
  12. Vivado设计流程(五)工程实现
  13. php7 编译安装,添加扩展 pdo /usr/local/php/bin/phpize 发现没有 configure
  14. Mac 入门教程:如何更改你的 Mac 设备名称
  15. 华硕开机画面修改_电脑开机密码忘记,进不了系统,一招教你轻松解决!
  16. AE开发之鹰眼窗口、书签
  17. git报错:remote: error: hook declined to update refs/heads/master
  18. 555定时器的工作原理
  19. MySQL练习题及答案
  20. JAVA修改运行内存

热门文章

  1. 翻译:Vim从入门到精通 Mac OS
  2. Git Push,Pull,Clone出现SSL certificate problem: unable to get local issuer certificate
  3. 计算机软件跨考教育学优点,2021教育学考研优势院校分析之:华南师范大学
  4. 决策树 结构_如何快速简单的理解决策树的概念?
  5. 批量识别图片大致不相同图片_一款非常优秀的文字识别工具,可以批量图片识别文字...
  6. java 任务链模式,flink部署运行架构
  7. C++ 标准模板库STL
  8. elkan K-Means
  9. 15投影矩阵与Moore-Penrose逆(1)
  10. leetcode 171. Excel Sheet Column Number