大家都知道事务管理是基于AOP的,对AOP还不了解的请自行百度。

实现一个事务需要以下几步:1.获取数据库连接  2.执行数据库操作  3.如果2步骤发生异常就回滚,否则就提交  4.释放资源。

然后1、3、4步骤是所有事务所共有的逻辑,程序员真正需要关心的只有第2步,spring的事务管理也正是帮我们做了1、3、4的工作。

那么,我们想在项目中使用spring事务需要做哪些准备呢?

第一步,要配置dataSource、transactionManager

<!--启动spring注解功能 -->
<tx:annotation-driven transactionmanager="transactionManager" /> <!-- 设定transactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <!-- 配置数据源,这里以C3P0为例 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="..."></property>
...
<property name="checkoutTimeout" value="..."/>
</bean>

第二步,在需要事务的方法上加上注解 @Transactional 即可。

是的,就是这么简单。下面通过分析源码来说明整个流程,我们先来看配置这个注解时可以设置哪些参数。

这些配置一般我们只关注传播属性、隔离级别和超时时间就行,具体不懂得自行百度。

我们再回过头来看前面配置的transactionManager(即DataSourceTransactionManager)继承关系图

我们只看PlatformTransactionManager-->AbstractPlatformTransactionManager-->DataSourceTransactionManager这条线,先来看PlatformTransactionManager接口:

它是spring事务管理器的顶层接口,这三个方法是对应前边所说的1、3两个步骤,分别为开启事务、提交、回滚。再来看它的实现类AbstractPlatformTransactionManager,主要看上边三个方法对应的子类实现,可以发现它们都是一样的套路:先做一些边界条件判断,然后调用自己内部的对应do...方法,而且每个对应的do...方法都是protected abstract 修饰的,实际上继承于AbstractPlatformTransactionManager 的类有很多,如DataSourceTransactionManager , JtaTransactionManager, HibernateTransactionManager 等,不管是哪一个种,总要有 getTransaction,commit,rollback的方法,所以这个三个方法在接口中,不过具体实现方式有所区别,所以具体的实现需要而且是必须在子类中完成。AbstractPlatformTransactionManager 规定了这三个方法的调用顺序,真正的细节在子类中以”do*”开头的方法中实现,这也正是大多数抽象类的作用所在。

我们先来看此抽象类的getTransaction方法:

重点关注我标注出来的两步,先调用doGetTransaction方法获取一个事务(新开启的或者已经存在的),然后调用doBegin方法开始执行事务。

我们直接去看具体实现类的这两个方法,这里以DataSourceTransactionManager为例。doGetTransaction方法其实就是返回我们配置的全局数据源。

再来看doBegin方法,先获取一个connetion,设置autoCommit为false,设置隔离级别...

然后调用 service 中的代码,如果没有抛出异常Spring框架将继续调用AbstractPlatformTransactionManager中的commit方法,继续看源码:

还是要看子类doCommit方法,先拿到连接(其实就是之前获取到的那个),然后commit,有异常就抛给上层。

这里先抛出两个问题:spring事务管理如何保证同一个线程总是能获取到已开启的事务?也就是说默认传播属性下同一个线程怎么保证最多只会有一个事务?

现在事务已经提交了,如果发生异常的话,父类中调用方法 rollback方法:

继续看子类doRollback方法:

现在回想我刚才抛出的两个问题,不知道你有没有发现,在提交和回滚的是获取当前连接都是这样调用:

Connection con = txObject.getConnectionHolder().getConnection();

那怎么保证每次get的是同一个连接?

来看的dobegin方法,重点关注bind the session holder这个方法

附上resource的定义,可以发现它是一个ThreadLocal。

private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<Map<Object, Object>>("Transactional resources");

现在明白了吧,spring在开启事务的时候会把获取到的连接绑定在事务的上下文环境中,然后把这个上下文环境又绑定在当前线程的ThreadLocal中,这样每个线程的事务就是独立的了,那同一个线程每次获取连接获取到的也就是同一个了。

最后看一下对资源释放的代码,父类中cleanupAfterCompletion方法调用了doCleanupAfterCompletion,我们直接看子类实现:

releaseConnection方法就是进行资源释放的工作,逻辑比较简单就不分析了。

至此 Spring 帮我们管理的事务,其主要流程和方法已经介绍完了,当然其中还有很多 private 方法和 protected 方法没有介绍。我想只要把主干捋清楚之后,其他一些方法也很好理解了。

绕了一大圈,实际上还是绕不过开启事务,提交事务,回滚事务三件事,只是这三件事情现在由Spring帮助我们在背后默默做好了。

spring事务管理源码解析--加了@Transactional注解后Spring究竟为我们做了哪些事情?的更多相关文章

  1. Spring 与 MyBatis 事务管理源码解析

    用到mybatis便由spring和myabtis集成,SqlSessionFactoryBean(直接负责对mybatis所需环境的创建) ,配置相应的datasource到springConfig ...

  2. Spring事务管理源码分析

    Spring事务管理方式 依据Spring.xsd文件可以发现,Spring提供了advice,annotation-driven,jta-transaction-manager3种事务管理方式.详情 ...

  3. Spring Security 解析(七) —— Spring Security Oauth2 源码解析

    Spring Security 解析(七) -- Spring Security Oauth2 源码解析   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因 ...

  4. spring boot @Value源码解析

    Spring boot 的@Value只能用于bean中,在bean的实例化时,会给@Value的属性赋值:如下面的例子: @SpringBootApplication @Slf4j public c ...

  5. Feign 系列(05)Spring Cloud OpenFeign 源码解析

    Feign 系列(05)Spring Cloud OpenFeign 源码解析 [TOC] Spring Cloud 系列目录(https://www.cnblogs.com/binarylei/p/ ...

  6. [源码解析] 深度学习分布式训练框架 horovod (3) --- Horovodrun背后做了什么

    [源码解析] 深度学习分布式训练框架 horovod (3) --- Horovodrun背后做了什么 目录 [源码解析] 深度学习分布式训练框架 horovod (3) --- Horovodrun ...

  7. iOS开发SDWebImage源码解析之SDWebImageManager的注解

    最近看了两篇博客,写得很不错,关于SDWebImage源码解析之SDWebImageManager的注解: 1.http://www.jianshu.com/p/6ae6f99b6c4c 2.http ...

  8. Spring注解Component原理源码解析

    在实际开发中,我们经常使用Spring的@Component.@Service.@Repository以及 @Controller等注解来实现bean托管给Spring容器管理.Spring是怎么样实 ...

  9. 【Spring实战】Spring注解配置工作原理源码解析

    一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...

随机推荐

  1. CF 1006C Three Parts of the Array【双指针/前缀和/后缀和/二分】

    You are given an array d1,d2,-,dn consisting of n integer numbers. Your task is to split this array ...

  2. 使用 Hibernate 完成 HibernateUtils 类 (适用于单独使用Hibernate或Struts+Hibernate)

    package com.istc.Utilities; import org.hibernate.Session; import org.hibernate.SessionFactory; impor ...

  3. python编码问题 与 代码换行问题

    转载请注明: 仰望高端玩家的小清新 http://www.cnblogs.com/luruiyuan/ python程序对于unicode码的支持情况不同 python3 支持较好,在文件开头加入如下 ...

  4. JavaScript防止重复提交表单

    往往有些用户网络慢或者其他问题,在提交表单的时候使劲点击保存提交按钮,在提交表单的时候加上下面的代码,即可以限制在一定时间内,只有一次点击是有效的. <script> var mypret ...

  5. Jenkins获取分支的插件

    Jenkins--->xxxx--->配置--->参数化构建过程--->选择Git Parameter Plug-In插件 Name: git_branch Descripti ...

  6. 传统路径导出 VS 直接路径导出(oracle exp direct=y)

    Oracle 传统的Export与Import依旧被保留到11g,而且9i与10g有很多依旧使用Export与Import方式进行备份与恢复的企业.从Oracle 7.3开始,传统的exp导出程序提供 ...

  7. Swift中混编OC第三方库

    现在Swift的第三方库还比较少,有时候需要使用OC的第三方库,其实也是很容易的.   我们使用如下步骤: 1.新建的Swift项目,第一次创建OC文件时会询问是否生成 桥接头,选择是的话会生成一个桥 ...

  8. 最新Mac安装CocoaPods详细教程及各种坑解决办法

    网上有很多教程,但要么内容很老,要么不详细,要么各种坑的情况没写.最近买新电脑了,正好要走一遍这些流程,所以写下次教程. 一.安装RVM及更新Ruby 安装RVM的目的是为了更新Ruby,如果你的Ru ...

  9. Java杂谈6——Java安全模型

    Java语言安全模型是其有别于传统的编程语言的一个很重要的特点,采用一种沙箱模型隔离了Java的运行环境与具体的操作系统,使得Java在网络环境下能够更为安全的运行.理解Java的安全模型,能够帮助我 ...

  10. ios如何实现被键盘遮挡时,带有textfield的tableview自动上移

    最正规的办法,用通知step 1:在进入视图的时候添加监视:(viewDidLoad什么的)   复制代码 // Observe keyboard hide and show notification ...