相信 Java 程序员一定听过大名鼎鼎的 Spring 框架,Spring 框架的低侵入性,模块间的解耦,DI 机制,AOP 特性等为程序员大大降低了程序开发的难度,可以说 Spring 目前是最流行的框架技术,占有很大一部分市场,为广大程序员带来巨大的便利。
今天来聊一聊 Spring 中的事务管理为我们做了些什么事。
由于 DI 和 AOP 是整个 Spring 框架的灵魂,而且今天要讲的事务管理也是基于 AOP 的,所以不明白的请先移步至:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
先看一段老代码:
上面代码是一个完整事务,缺点很明显,就是比较啰嗦。实际上要做的事情只有向数据库中插入一条数据。如果每组 jdbc 操作都要去控制 connection,preparedStatement 的状态,一堆的 try-catch-finally 代码,看起来很头疼,而且非常容易写错,忘记关闭 connection 也会导致资源耗尽这样致命的错误。
程序员都是很懒的,这些复杂的重复率非常高的代码最好能交给某个模版去帮忙管理,用技术干掉重复的事情,从而能更好的专注于自己的业务代码。
下面介绍一下 Spring 怎样去帮助我们管理了上面这些状态,先看一下 Spring 中声明式事务能简化到怎样的程度。
1、需要配置 dataSource, transactionManager, sqlSessionFactory, MapperScannerConfigurer
2、业务逻辑中事务的入口
看上去使用 Spring 帮我们管理的事务是非常简单的,而且业务代码简洁到了极致。
只要我们配置好了相应的 dataSource,transactionManager, sqlSessionFactory, MapperScannerConfigurer,
在需要事务的方法上加上注解 @Transactional 即可。
@Transactional 这个注解里面有几个属性用来描述程序员需要的事务类型。
看一下源码里面有如下几个属性:
那么 Spring 框架是怎样做到的呢?
@Transactional 这个注解的背后有怎样的逻辑呢?
为什么那些复杂的状态控制代码,资源释放的代码都看不到了呢?
将一组 jdbc 操作放入同一个事务需要的几个步骤:
开启事务
jdbc 操作
成功:提交事务
异常:回滚事务
释放资源
实际上,正是 Spring 帮助我们做了上述1,3,4三步(第2步使我们的业务代码)。看一下之前配置的 transactionManager 以及其父类AbstractPlatformTransactionManager,AbstractPlatformTransactionManager继承了接口 PlatformTransactionManager,而该接口中只有三个方法:
可以看出这三个方法分别对应了1)3)两个步骤。
继续看抽象类 AbstractPlatformTransactionManager 中的方法,AbstractPlatformTransactionManager 的方法有很多,其中有几个方法值得注意:
看到这里1)3)4)三步都有了方法对应。每个 public 方法又对应了以“do*”开头各自 protected abstract 方法,这是什么意思呢?
实际上继承于AbstractPlatformTransactionManager 的类有很多,如DataSourceTransactionManager , JtaTransactionManager, HibernateTransactionManager 等,不管是哪一个种,总要有 getTransaction,commit,rollback的方法,所以这个三个方法在接口中,不过具体实现方式有所区别,所以具体的实现需要而且是必须在子类中完成。
AbstractPlatformTransactionManager 规定了这三个方法的调用顺序,真正的细节在子类中以”do*”开头的方法中实现,这也正是大多数抽象类的作用所在。
当然 AbstractPlatformTransactionManager 中还有很多其它方法,我们这里只关注最重要的几个主干上方法,也就是接口中规定的方法以及其子类中的实现,这里子类选取 DataSourceTransactionManager 进行代码分析。
首先先结合时序图看一下几个主要方法的调用:
下面与源码相结合,先看一下入口处,也就是 AbstractPlatformTransactionManager 中 getTransaction(TransactionStatus status) 方法:
跳转到子类 doBegin(…) 方法中:
此时 Spring 框架已经帮我们获取了一个 connetion,并且设置了 autoCommit 为 false。之后调用 service 中的代码,如果一切顺利 Spring 框架将继续调用 AbstractPlatformTransactionManager 中的 commit(…) 方法,继续看源码:
跳转到子类看下 doCommit(…) 方法具体做了些什么事情:
现在事务已经提交了,如果是异常的情况,父类中调用方法 robbback(…):
跳转到子类中看 doRollback(…) 的具体实现:
最后看一下对资源释放的代码,父类中 cleanupAfterCompletion(status) 方法调用了 doCleanupAfterCompletion(...):
至此 Spring 帮我们管理的事务,其主要流程和方法已经介绍完了,当然其中还有很多 private 方法和 protected 方法没有介绍。我想只要把主干捋清除之后,其他一些方法也很好理解了。
绕了一大圈,实际上还是绕不过开始事务,提交事务,回滚事务三件事,只是这三件事情现在由 Spring 帮助我们在背后默默做好了。
应该感谢 Spring 帮助程序员做了这些繁琐的事情,可以让程序员不用担心资源泄露之类事情,专注于自己的业务逻辑代码。同时 Spring 框架代码规范,设计清晰也是程序员非常值得借鉴的地方。
本文作者:谢星星(点融黑帮),目前就职于点融网架构组Backend Architecture团队,简单务实的程序猿,写代码不给自己挖坑更不能留坑于他人。