Spring Tx源码解析(二)

前言

  上一篇我们介绍了spring-tx中的底层抽象,本篇我们一起来看看围绕这些抽象概念spring-tx是如何打造出声明式事务的吧。笼统的说,spring-tx-5.2.6.RELEASE的实现主要分为两个部分:

  1. PlatformTransactionManager抽象下的事务管理细节
  2. 基于spring-aop的拦截器如何将普通方法增强为事务方法的

这两部分彼此独立又相互成就,并且每个部分都有着大量的源码支撑,本篇我们先来分析spring-tx中的AOP部分吧。

一切从EnableTransactionManagement说起

  EnableTransactionManagement注解想必大家都很熟悉了,它是启用 Spring 中注释驱动的事务管理功能的关键。

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
// 使用cglib subclassing策略还是jdk dynamic proxy
boolean proxyTargetClass() default false;
// 使用spring aop运行时代理机制还是aspect j的静态编译策略
AdviceMode mode() default AdviceMode.PROXY;
// 事务Advisor的优先级
int order() default Ordered.LOWEST_PRECEDENCE;
}

EnableTransactionManagement注解的主要作用是向容器中导入TransactionManagementConfigurationSelector,至于注解中定义的几个属性在Spring AOP源码解析中有过详细分析,这里就不再赘述了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
// AutoProxyRegistrar的作用是开启AOP相关功能
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}

private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}

由于我们并没有使用AspectJ,因此导入容器的自然是ProxyTransactionManagementConfiguration这个配置类。

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
@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {

@Nullable
protected AnnotationAttributes enableTx;

@Nullable
protected TransactionManager txManager;

@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
// 获取EnableTransactionManagement注解中定义的属性
this.enableTx = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
if (this.enableTx == null) {
throw new IllegalArgumentException("@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
}
}
// 通过TransactionManagementConfigurer获取事务管理器
@Autowired(required = false)
void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
}
TransactionManagementConfigurer configurer = configurers.iterator().next();
this.txManager = configurer.annotationDrivenTransactionManager();
}
// 支持事务相关事件的pub-sub功能
@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
return new TransactionalEventListenerFactory();
}
}

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
// 向容器中注入一个关于事务的Advisor
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}

// 事务属性的提取器,用来解析@Transactional注解
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}

// 事务拦截器
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}

  这个配置类的核心是向容器中导入一个类型为BeanFactoryTransactionAttributeSourceAdvisor的Bean。这是一个PointcutAdvisor,它的PointcutTransactionAttributeSourcePointcutAdviceTransactionInterceptor

  TransactionAttributeSourcePointcut利用TransactionAttributeSource解析@Transactional注解的能力来选取标注了@Transactional注解的方法,而TransactionInterceptor则根据应用提出的需求(来自对@Transactional注解的解析)将方法增强为事务方法,因此BeanFactoryTransactionAttributeSourceAdvisor可以识别出那些标注了@Transactional注解的方法,为它们应用上事务相关功能。

事务拦截器的工作原理

TransactionAttributeSource

  TransactionInterceptor能对方法进行增强,但是它却不知道该如何增强,比如是为方法新开一个独立事务还是沿用已有的事务?什么情况下需要回滚,什么情况下不需要?必须有一个『人』告诉它该如何增强,这个『人』便是TransactionAttributeSource

  @Transactional注解定义了事务的基础信息,它表达了应用程序期望的事务形态。TransactionAttributeSource的主要作用就是解析@Transactional注解,提取其属性,包装成TransactionAttribute,这样TransactionInterceptor的增强便有了依据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface TransactionAttributeSource {
/**
* 测试某个类是否有可能解析出TransactionAttribute,对于不可能解析出事务属性的类,没有必要
* 调用 #getTransactionAttribute(...),从而提升性能
*/
default boolean isCandidateClass(Class<?> targetClass) {
return true;
}

/**
* 返回方法的事务属性,由@Transactional注解指定
*/
@Nullable
TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}
tx-attr-source

前面我们已经见过,spring-tx使用AnnotationTransactionAttributeSource来做具体的解析工作,其父类AbstractFallbackTransactionAttributeSource定义了解析TransactionAttribute的优先级,核心方法是computeTransactionAttribute(...)

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
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 不解析非public修饰的方法,这也是@Transactional注解失效的一个原因
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// 传入的方法可能来自于接口,相比较而言目标类中定义的方法具有更高的优先级
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 代理给模板方法
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// 尝试在目标类上寻找
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
// 传入的方法确实不是目标类中定义的那一个
if (specificMethod != method) {
// 尝试在传入的方法上获取,这也是@Transactional注解支持定义在接口中的原因
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// 最后尝试在定义方法的类上获取,通常来说是接口本身
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}

AnnotationTransactionAttributeSource默认只解析public修饰的方法,这也是导致@Transactional注解失效的一个原因,除此之外它还实现了父类中定义的两个模板方法:

  1. findTransactionAttribute(Method method),获取定义在方法上的事务属性

  2. findTransactionAttribute(Class<?> clazz),获取定义在类上的事务属性

同时为了支持 EJB 中定义的javax.ejb.TransactionAttribute和 JTA 中定义的javax.transaction.Transactional注解,AnnotationTransactionAttributeSource选择将实际的提取工作代理给TransactionAnnotationParser。Spring 提供的@Transactional注解由SpringTransactionAnnotationParser进行解析。

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
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {

@Override
public boolean isCandidateClass(Class<?> targetClass) {
return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);
}

@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
// 使用 find 语义来提取 Transactional 注解
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
// 将 Transactional 注解包装成 TransactionAttribute
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));

List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);

return rbta;
}

@Override
public boolean equals(@Nullable Object other) {
return (this == other || other instanceof SpringTransactionAnnotationParser);
}

@Override
public int hashCode() {
return SpringTransactionAnnotationParser.class.hashCode();
}
}

SpringTransactionAnnotationParser的源码还是很简单的,它使用AnnotatedElementUtils工具类定义的find语义来获取@Transactional注解信息。RuleBasedTransactionAttributerollbackOn(...)的实现还是挺有意思的,其它的都平平无奇。

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
@Override
public 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;

// 选取最接近的 RollbackRuleAttribute
if (this.rollbackRules != null) {
for (RollbackRuleAttribute rule : this.rollbackRules) {
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}

if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}

// 没有RollbackRuleAttribute支持此异常
if (winner == null) {
// 回退到父类的处理方式 —> (ex instanceof RuntimeException || ex instanceof Error)
// 只要是运行时异常或Error都进行回滚
logger.trace("No relevant rollback rule found: applying default rules");
return super.rollbackOn(ex);
}
// 找到了支持此异常的 RollbackRuleAttribute
// 确保它不是 NoRollbackRuleAttribute,NoRollbackRuleAttribute表示的是遇到类型的异常时不回滚
return !(winner instanceof NoRollbackRuleAttribute);
}

RollbackRuleAttribute是用来确定在发生特定类型的异常(或其子类)时是否应该回滚,而NoRollbackRuleAttribute继承自RollbackRuleAttribute,但表达的是相反的含义。RollbackRuleAttribute持有某个异常的名称,通过getDepth(Throwable ex)算法来计算指定的Throwable和持有的异常在继承链上的距离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int getDepth(Throwable ex) {
return getDepth(ex.getClass(), 0);
}

private int getDepth(Class<?> exceptionClass, int depth) {
// 名称匹配
if (exceptionClass.getName().contains(this.exceptionName)) {
return depth;
}
// 不在一个继承体系
if (exceptionClass == Throwable.class) {
return -1;
}
// 沿着继承链向上比对
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
TransactionInterceptor
tx-advice

  程序猿只有在拿到需求以后才能开工,TransactionInterceptor也一样,有了TransactionAttributeSource之后就可以有依据的增强了。观察类图,TransactionInterceptor实现了MethodInterceptor接口,那么自然要实现接口中的方法:

1
2
3
4
5
6
7
8
9
10
 @Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取目标Class
Class<?> targetClass = (invocation.getThis() != null ?
AopUtils.getTargetClass(invocation.getThis())
: null);
// 代理给基类的 #invokeWithinTransaction(...)
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

可以看到,TransactionInterceptor本身是没有实现任何逻辑的,它更像一个适配器。这样分层以后,TransactionAspectSupport理论上就可以支持任意类型的Advice而不只是MethodInterceptor。实现上TransactionAspectSupport确实也考虑了这一点,我们马上就会看到。

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
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 解析 @Transactional 注解,获取事务属性
// 如果不存在事务属性,说明这个方法不是一个事务方法
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ?
tas.getTransactionAttribute(method, targetClass) : null);
// 从容器中选取一个 TransactionManager 来管理事务
final TransactionManager tm = determineTransactionManager(txAttr);

// 1. 略去了 ReactiveTransactionManager 相关内容,不使用webflux的话用不上
// 2. 略去了 CallbackPreferringPlatformTransactionManager 相关内容,除了基于回调,几乎和
// PlatformTransactionManager是一模一样的,限于篇幅,删减掉了

PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 方法标识,后续用作事务名称,多用于debug
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

// 开启事务(尽可能)
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

Object retVal;
try {
// 转向拦截器链中的下一个拦截器
// 由于TransactionInterceptor的优先级默认情况下是最低的,基本上这就是调用目标方法了
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 出现异常时对事务的处理 ——> 根据rollbackFor、noRollbackFor来决定是提交还是回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 执行清理工作
cleanupTransactionInfo(txInfo);
}
// 方法正常执行完时提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}

invokeWithinTransaction(...)的流程还是非常清晰的:

  1. 获取TransactionManagerTransactionAttribute,这两兄弟一个决定了能否开启事务,另一个决定了如何开启事务
  2. 尝试开启事务
  3. try块包裹住目标方法的执行,根据执行结果进行相应的处理——提交或回滚

第一步前文已经分析过了,我们来看第二步。

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
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 如果未指定事务名称,在这里指定一下
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) { // 代理模式,重新保证一下
@Override
public String getName() {
return joinpointIdentification;
}
};
}

TransactionStatus status = null;
// txAttr和tm两兄弟都存在的情况下才能开启事务
if (txAttr != null) {
if (tm != null) {
// 根据txAttr的相关属性开启事务
// 这里就是PlatformTransactionManager事务管理的核心逻辑了,我们下篇再说
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 准备TransactionInfo
// 如果不考虑支持不同类型的Advice的话,TransactionInfo其实是不必要的
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
// 创建一个新的txInfo
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
// txAttr存在时才可能获取到TransactionStatus,这也表示是一个事务方法
if (txAttr != null) {
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// 记录下事务的状态
txInfo.newTransactionStatus(status);
}
else {
// 非事务方法
if (logger.isTraceEnabled()) {
logger.trace("No need to create transaction for [" + joinpointIdentification +
"]: This method is not transactional.");
}
}
// 绑定到当前线程,以此来支持各种不同类型的Advice
// 比如我们使用BeforeAdvice和AfterAdvice来模拟MethodInterceptor
// 这两Advice都继承自TransactionAspectSupport,TransactionStatus的传递就得依赖于线程私有存储了
// TransactionAspectSupport因此也提供了 #currentTransactionStatus() 方法来获取当前的事务状态
txInfo.bindToThread();
return txInfo;
}

TransactionInfo是一个非常简单的类,我们就不费什么笔墨去分析它了。接着看第三步,这一步涉及到两个不同的操作——提交或回滚。

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
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
// 如果存在事务
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// 使用 TransactionManager 进行提交
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}

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);
}
// 查询 rollbackOn 相关配置,如果确实需要回滚的话
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 使用 TransactionManager 进行回滚
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 {
// 不需要回滚,那么转而提交事务
// 当然了,调用 commit(...) 并不能保证事务一定被提交,还有一些因素会导致事务回滚,比如
// 标记为 rollback-only 的事务,我们下篇再说
try {
// 使用 TransactionManager 进行提交
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;
}
}
}
}

至此,TransactionInterceptor于我们而言已经没有任何秘密了。

后记

  本篇我们一起分析了spring-tx是如何通过spring-aop的拦截器将普通方法增强为事务方法的,下篇就该说道说道PlatformTransactionManager抽象下的事务管理细节啦,我们下篇再见~~


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!