Spring事务部分源码解析(二) - 事务管理器

按照上篇流程详解

获取方法的Transaction配置

按照上篇对于几个实体类的描述,可以发现注解@Transactional的属性的字段和TransactionDefinition大致比较相似,但又不完全一致.

从@Transactional开始讲起

这个显然很常见,就是我们平时需要事务时用到的注解.可是spring如何去解析?我们看一下使用到这个注解的地方,排除掉在test和comment下的使用.

进入方法

TransactionAttribute正是上篇文章中TransactionDefinition的子类

返回的RuleBasedTransactionAttribute我们看一下他的继承图

我们打开源码看一下RuleBasedTransactionAttribute和父类DefaultTransactionAttribute晚上寻找发现,事务常规的设置是继承DefaultTransactionDefinition来注入,rollback相关则实现TransactionAttribute.这就是我们上篇提到TransactionAttribute和TransactionDefinition责任的划分

我们网上追溯,调用方

AnnotationTransactionAttributeSource.findTransactionAttribute()

AbstractFallbackTransactionAttributeSource.computeTransactionAttribute

AnnotationTransactionAttributeSource.getTransactionAttribute()

TransactionAspectSupport.invokeWithinTransaction

获取Transaction配置流程:根据方法和类确定唯一key,从缓存中获取.如果获取不到则通过TransactionAnnotationParser解析方法来获取事务属性.并放置缓存中

获取事务管理器(PlatformTransactionManager)

接口定义方法(可以发现极其精简干净)

抽象子类AbstractPlatformTransactionManager, 可以发现多了很多方法.但并没有实现,用于子类继承处理

子类

我们可以关注一下DataSourceTransactionManager和RabbitTransactionManager,是抽象类的一个实现.DataSourceTransactionManager的具体实现是通过Java提供的接口实现.而RabbitTransactionManager则是自己实现(毕竟已经属于定制了)

1
此处可以回顾TransactionAspectSupport.invokeWithinTransaction(), 开启事务后上下文通过TransactionInfo传递信息. 每当设计事务提交或回滚等相关,都是txInfo.getTransactionManager().commit()/rollback()

AbstractPlatformTransactionManager.rollback()

大致说一下回滚流程.

  1. 触发TransactionSynchronization完成前事件
  2. 事务具体实现的真正回滚
  3. 触发TransactionSynchronization完成后事件
  4. 标记完成,执行后续清理操作
1
ps: 如果一个方法需要执行多个事务如何处理?(剧透一下:TransactionSynchronization)

AbstractPlatformTransactionManager.commit()

从图中看出在某种情况会执行回滚操作.最开始看到这里的时候也迷过.后来在一次事务测试时,同一个事务,方法嵌套时,内部方法抛异常,但是外部方法捕获正常处理.一部分觉得内部回滚外部不回滚.实际结果是内外都会回滚和是否捕获并没有关系. 可以回顾一下processRollback()中的status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()判断

1
private boolean globalRollbackOnParticipationFailure = true;

默认为true,也就是说在非特意设置的情况下会执行doSetRollbackOnly(status);将rollbackOnly设置为true.在也就是说当内部内部方法发生异常之后会上下文TransactionStatus.rollbackOnly为true,执行的便是processRollback().这也就解释为什么内部方法捕获之后依旧会回滚的原因/

1
ps: 本身在同一个事务,内部方法发生异常执行回滚,理应是整个事务回滚而不是部分回滚.以为捕获异常处理之后再提交这种思想本身就不对噢!!!

如果没有发生异常.则执行正常提交流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}

// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}

}
finally {
cleanupAfterCompletion(status);
}
}

执行流程

  1. 执行预提交处理
  2. 触发提交前事件
  3. 触发完成事件
  4. 提交
  5. 触发提交事件
  6. 触发提交完成事件
  7. 清除状态
1
个人理解的第一步是预留分布式事务接口. 而后续的触发时间则是扩展处理(后续讲TransactionSynchronization会聊到),不过这个扩展处理划分的也太细了

AbstractPlatformTransactionManager.cleanupAfterCompletion()

其实这里也没什聊的,打开方法的源码基本能发现都是清除上下文状态.也代表事务的结束

总结

  1. AbstractPlatformTransactionManager实现了核心的逻辑,每一个核心的逻辑的具体实现又留给各个厂商实现.这一部分挺像AbstractQueuedSynchronizer的.设计挺好的.扩展性好,具体的逻辑又帮你实现好.只需要继承定制实现自己非常简单的逻辑就好
  2. 事务嵌套情况通过线程上线文来处理
  3. 多事务通过触发事件处理(后续讲TransactionSynchronization会聊到),扩展性真的挺好
1
下一篇讲触发事件处理.相关类TransactionSynchronizationManager, TransactionSynchronizationUtils, TransactionSynchronization

Spring事务部分源码解析(二) - 事务管理器
https://gallrax.github.io/2019/09/16/Spring事务部分源码解析(二)-事务管理器/
作者
Gallrax
发布于
2019年9月16日
许可协议