前言
本专题大纲如下:
对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细,本来是想在源码分析的文章中附带讲一讲事务使用中的问题,这两天想了想还是单独写一篇并作为事务专题的收尾篇,也是我Spring源码专题的收尾篇。
本文大纲如下:
在看这篇文章,以及下篇源码分析的文章我希望你对Spring AOP以及有充分的了解,不然一些细节问题你可能看不明白,关于Spring AOP如果你能看完这三篇文章基本上就没什么问题了
Spring官网阅读(十八)AOP的核心概念
Spring中AOP相关的API及源码解析,原来AOP是这样子的
你知道Spring是怎么将AOP应用到Bean的生命周期中的吗?
编程式事务
Spring提供了两种编程式事务管理的方法
- 使用 TransactionTemplate 或者 TransactionalOperator.
- 直接实现TransactionManager接口
如果是使用的是命令式编程,Spring推荐使用TransactionTemplate 来完成编程式事务管理,如果是响应式编程,那么使用TransactionalOperator更加合适。
TransactionTemplate
使用示例(我这里直接用的官网提供的例子了)
public class SimpleService implements Service { private final TransactionTemplate transactionTemplate; // 使用构造对transactionTemplate进行初始化 // 需要提供一个transactionManager public SimpleService(PlatformTransactionManager transactionManager) { this.transactionTemplate = new TransactionTemplate(transactionManager); } public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { // 这里实现自己的相关业务逻辑 updateOperation1(); return resultOfUpdateOperation2(); } }); } }
在上面的例子中,我们显示的使用了TransactionTemplate来完成事务管理,通过实现TransactionCallback接口并在其doInTransaction方法中完成了我们对业务的处理。我们可以大概看下TransactionTemplate的execute方法的实现:
public <T> T execute(TransactionCallback<T> action) throws TransactionException { Assert.state(this.transactionManager != null, "No PlatformTransactionManager set"); if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) { return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action); } else { // 1.通过事务管理器开启事务 TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { // 2.执行传入的业务逻辑 result = action.doInTransaction(status); } catch (RuntimeException | Error ex) { // 3.出现异常,进行回滚 rollbackOnException(status, ex); throw ex; } catch (Throwable ex) { // 3.出现异常,进行回滚 rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); } // 4.正常执行完成的话,提交事务 this.transactionManager.commit(status); return result; } }
这些方法具体的实现我们暂且不看,后续进行源码分析时都会详细介绍,之所以将这个代码贴出来是让大家更好的理解TransactionTemplate的工作机制:实际上就是通过一个TransactionCallback封装了业务逻辑,然后TransactionTemplate会在事务的上下文中调用。
在上面的例子中doInTransaction是有返回值的,而实际上有时候并不需要返回值,这种情况下,我们可以使用TransactionCallbackWithoutResult提代TransactionCallback。
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
实际上我们还可以通过TransactionTemplate指定事务的属性,例如隔离级别、超时时间、传播行为等等
TransactionTemplate是线程安全的,我们可以全局配置一个TransactionTemplate,然后所有的类都共享这个TransactionTemplate。但是,如果某个类需要特殊的事务配置,例如需要定制隔离级别,那么我们就有必要去创建不同的TransactionTemplate。
TransactionOperator
TransactionOperator适用于响应式编程的情况,这里就不做详细介绍了
TransactionManager
实际上TransactionTemplate内部也是使用TransactionManager来完成事务管理的,我们之前也看过它的execute方法的实现了,其实内部就是调用了TransactionManager的方法,实际上就是分为这么几步
- 开启事务
- 执行业务逻辑
- 出现异常进行回滚
- 正常执行则提交事务
这里我还是直接用官网给出的例子
// 定义事务 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // txManager,事务管理器 // 通过事务管理器开启一个事务 TransactionStatus status = txManager.getTransaction(def); try { // 完成自己的业务逻辑 } catch (MyException ex) { // 出现异常,进行回滚 txManager.rollback(status); throw ex; } // 正常执行完成,提交事务 txManager.commit(status);
我们在后边的源码分析中其实重点分析的也就是TransactionManager的源码。
申明式事务
在对编程式事务有一定了解之后我们会发现,编程式事务存在下面几个问题:
1.我们的业务代码跟事务管理的代码混杂在一起。
2.每个需要事务管理的地方都需要写重复的代码
如何解决呢?这就要用到申明式事务了,实现申明式事务一般有两种方式
- 基于XML配置
- 基于注解
申明式事务事务的实现原理如下(图片来源于官网):
实际上就是结合了APO自动代理跟事务相关API。通过开启AOP自动代理并向容器中注册了事务需要的通知(Transaction Advisor),在Transaction Advisor调用了事务相关API,其实内部也是调用了TransactionManager的方法。
基于XML配置这种方式就不讲了,笔者近两年时间没用过XML配置,我们主要就看看通过注解方式来实现申明式事务。主要涉及到两个核心注解
- @EnableTransactionManagement
- @Transactional
@EnableTransactionManagement这个注解主要有两个作用,其一是,开启AOP自动代理,其二是,添加事务需要用到的通知(Transaction Advisor),如果你对AOP有一定了解的话那你应该知道一个Advisor实际上就是一个绑定了切点(Pointcut)的通知(Advice),通过@EnableTransactionManagement这个注解导入的Advisor所绑定的切点就是通过@Transactional来定义的。
申明式事务的例子我这里就省去了,我相信没几个人不会用吧…