Spring 源码阅读 73:@EnableTransactionManagement 分析

简介: 【1月更文挑战第8天】本文通过源码分析了 @EnableTransactionManagement 注解开启 Spring 事务管理的原理。


基于 Spring Framework v5.2.6.RELEASE

概述

开发 Spring 应用时,通过在配置类上添加 @EnableTransactionManagement 注解,可以开启开启注解驱动的事务管理能力,被 @Transactional 注解标注的类或者方法,将会在一个数据库事务中执行。

本文将从 @EnableTransactionManagement 注解的源码出发,分析它是如何开启事务管理的。

EnableTransactionManagement 分析

进入 EnableTransactionManagement 的源码。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented@Import(TransactionManagementConfigurationSelector.class)
public@interfaceEnableTransactionManagement {
booleanproxyTargetClass() defaultfalse;
AdviceModemode() defaultAdviceMode.PROXY;
intorder() defaultOrdered.LOWEST_PRECEDENCE;
}

@EnableTransactionManagement 注解包含三个属性。

proxyTargetClass 属性

proxyTargetClass属性表示是否强制采用 CGLIB 的方式来创建代理对象。在 Spring 中,事务管理是通过 AOP 特性来实现的,而 AOP 是通过对 Bean 的代理来实现的,包括 JDK 动态代理和 CGLIB 代理,默认情况下 Spring 会优先采用 JDK 动态代理的方式,在无法采用 JDK 动态代理的时候,则采用 CGLIB 的方式。

默认情况下,proxyTargetClass的值为false,如果将其配置为true,则所有的代理对象都会通过 CGLIB 的方式来创建。

值得注意的是,如果这里将proxyTargetClass配置为true,那么,不仅被标记了 @Transactional 注解的类以及其标记的方法所在的类会被强制采用 CGLIB 创建代理对象,Spring 容器中所有需要创建代理的 Bean 都会被强制采用 CGLIB 的方式来创建代理。

mode 属性

mode的值是一个 AdviceMode 类型的枚举,默认值是AdviceMode.PROXY。它表示只能通过代理模式执行拦截的逻辑,这是通常情况下会采用的值。

order 属性

order属性表示事务相关的增强的顺序,默认是Ordered.LOWEST_PRECEDENCE,也就是最小值。

TransactionManagementConfigurationSelector 分析

除了以上三个配置属性外,@EnableTransactionManagement 通过 @Import 元注解,倒入了配置类型 TransactionManagementConfigurationSelector。

先看它的继承关系。

它是一个 ImportSelector 的实现类,先介绍一下实现了 ImportSelector 的类型,是如何在 Spring 的配置中起作用的。

当 Spring 扫描到应用中的某个配置类上被标记了 @EnableTransactionManagement 注解时,会通过它的 @Import 元注解找到其导入的类型 TransactionManagementConfigurationSelector,然后,判断出它是 ImportSelector 的实现类,就会调用它的selectImports方法,执行其中的配置逻辑。

此处被调用的是参数类型为 AnnotationMetadata 的selectImports方法,被调用时传入的参数封装了 @EnableTransactionManagement 注解的元信息。但是,TransactionManagementConfigurationSelector 并没有实现这个方法,因此,它继承了父类 AdviceModeImportSelector 中该方法的实现。

// org.springframework.context.annotation.AdviceModeImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata)@OverridepublicfinalString[] selectImports(AnnotationMetadataimportingClassMetadata) {
Class<?>annType=GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Assert.state(annType!=null, "Unresolvable type argument for AdviceModeImportSelector");
AnnotationAttributesattributes=AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (attributes==null) {
thrownewIllegalArgumentException(String.format(
"@%s is not present on importing class '%s' as expected",
annType.getSimpleName(), importingClassMetadata.getClassName()));
   }
AdviceModeadviceMode=attributes.getEnum(getAdviceModeAttributeName());
String[] imports=selectImports(adviceMode);
if (imports==null) {
thrownewIllegalArgumentException("Unknown AdviceMode: "+adviceMode);
   }
returnimports;
}

其中比较核心的逻辑就是从注解元信息中,获取到mode属性的值,然后将其作为参数调用了另外一个selectImports方法,这是一个模版方法,可以在 TransactionManagementConfigurationSelector 中找到方法的实现。

// org.springframework.transaction.annotation.TransactionManagementConfigurationSelector#selectImports@OverrideprotectedString[] selectImports(AdviceModeadviceMode) {
switch (adviceMode) {
casePROXY:
returnnewString[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
caseASPECTJ:
returnnewString[] {determineTransactionAspectClass()};
default:
returnnull;
   }
}

在默认情况下,也就是switch语句的PROXY分支,会返回 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 的类名称。

前面交代了,这个方法是在 Spring 扫描和处理配置类的时候被调用的,当返回这两个类名称后,Spring 会再解析这里两个类。

接下来我们再分别来看这两个类。

AutoProxyRegistrar 分析

AutoProxyRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,因此,当 Spring 加载到它之后,会执行其中的registerBeanDefinitions方法,执行一些注册 BeanDefinition 的逻辑。

@OverridepublicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata, BeanDefinitionRegistryregistry) {
booleancandidateFound=false;
Set<String>annTypes=importingClassMetadata.getAnnotationTypes();
for (StringannType : annTypes) {
AnnotationAttributescandidate=AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate==null) {
continue;
      }
Objectmode=candidate.get("mode");
ObjectproxyTargetClass=candidate.get("proxyTargetClass");
if (mode!=null&&proxyTargetClass!=null&&AdviceMode.class==mode.getClass() &&Boolean.class==proxyTargetClass.getClass()) {
candidateFound=true;
if (mode==AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
            }
         }
      }
   }
// 此处省略 LOG 代码}

这个方法中,依然可以获取到 @EnableTransactionManagement 的注解元数据,通过从中获取modeproxyTargetClass属性的值,执行后续的操作。

mode的值是AdviceMode.PROXY的时候,会执行 AopConfigUtils 的registerAutoProxyCreatorIfNecessary方法。从这个方法名可以看出,它的作用是创建用于给 Bean 创建代理对象的后处理器,因为 Spring 中的事务管理是依赖 AOP 特性的,因此,开启事务管理之前要先确保开启的 AOP 功能。

如果proxyTargetClass的值是true的话,还会再额外执行 AopConfigUtils 的forceAutoProxyCreatorToUseClassProxying方法,它的作用是找到 Spring 容器中用来创建 AOP 代理对象的后处理器的 BeanDefinition,并为其添加proxyTargetClass属性,值为true,这样,在为 Bean 实例创建代理对象时,就会直接采用 CGLIB 的方式。

总结,AutoProxyRegistrar 中的配置逻辑,其实就是为了确保 Spring 在开启事务管理之前,开启了 AOP 特性。

ProxyTransactionManagementConfiguration 分析

ProxyTransactionManagementConfiguration 是一个标记了 @Configuration 注解的配置类,Spring 解析它的时候,会获取到 @Bean 标记的方法,并在容器中注册对应的 BeanDefinition。

@Configuration(proxyBeanMethods=false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicclassProxyTransactionManagementConfigurationextendsAbstractTransactionManagementConfiguration {
@Bean(name=TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicBeanFactoryTransactionAttributeSourceAdvisortransactionAdvisor(
TransactionAttributeSourcetransactionAttributeSource, TransactionInterceptortransactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisoradvisor=newBeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx!=null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
      }
returnadvisor;
   }
@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicTransactionAttributeSourcetransactionAttributeSource() {
returnnewAnnotationTransactionAttributeSource();
   }
@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicTransactionInterceptortransactionInterceptor(TransactionAttributeSourcetransactionAttributeSource) {
TransactionInterceptorinterceptor=newTransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager!=null) {
interceptor.setTransactionManager(this.txManager);
      }
returninterceptor;
   }
}

在这个配置类中,通过 @Bean 方法,注册了三个 Bean,其中 BeanFactoryTransactionAttributeSourceAdvisor 依赖另外两者,也是三者中扮演重要角色的一个。从它的名字就可以看出,它是一个 Advisor 实现类,也意味着事务管理的相关增强逻辑、增强逻辑与类和方法的匹配,都是通过它来执行的。

AbstractTransactionManagementConfiguration 分析

除此之外,ProxyTransactionManagementConfiguration 的父类 AbstractTransactionManagementConfiguration 同样是一个 @Configuration 注解标记的配置类,其中有两个配置相关的方法。

@Autowired(required=false)
voidsetConfigurers(Collection<TransactionManagementConfigurer>configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
   }
if (configurers.size() >1) {
thrownewIllegalStateException("Only one TransactionManagementConfigurer may exist");
   }
TransactionManagementConfigurerconfigurer=configurers.iterator().next();
this.txManager=configurer.annotationDrivenTransactionManager();
}

setConfigurers方法是一个被标记了 @Autowired 注解的方法,参数重传入了 TransactionManagementConfigurer 类型的集合configurers。在方法体中,如果集合为空,则不做任何处理,如果集合元素数大于 1,则抛出异常,最后,调用集合中唯一的元素的annotationDrivenTransactionManager方法,得到一个 TransactionManager 对象, 赋值给成员变量txManager

从上述的逻辑中可以看出,这个方法用来加载 TransactionManager 配置。因此,我们可以创建一个类,通过实现 TransactionManagementConfigurer 接口及其 annotationDrivenTransactionManager 方法,来通过代码配置 TransactionManager。只需将这个类型作为一个 Bean 注册到 Spring 容器中,即可在此处被加载到。

@Bean(name=TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicstaticTransactionalEventListenerFactorytransactionalEventListenerFactory() {
returnnewTransactionalEventListenerFactory();
}

最后,还通过 @Bean 方法的方式,注册了一个 TransactionalEventListenerFactory 类型的 Bean。

总结

到这里,我们就知道了 @EnableTransactionManagement 是如何开启事务管理的。首先,它提供了几个配置属性,并引入了 TransactionManagementConfigurationSelector 配置类;然后,在 TransactionManagementConfigurationSelector 中,又引入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,它们两者的作用是确保 Spring 已经开启了 AOP 特性以及向容器中注册用于处理事务管理的 Advisor。

目录
相关文章
|
19天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
31 0
|
22天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
1月前
|
消息中间件 NoSQL Java
Spring Cloud项目实战Spring Cloud视频教程 含源码
Spring Cloud项目实战Spring Cloud视频教程 含源码
33 1
|
2月前
|
设计模式 Java Spring
【Spring源码】WebSocket做推送动作的底层实例是谁
我们都知道WebSocket可以主动推送消息给用户,那做推送动作的底层实例究竟是谁?我们先整体看下整个模块的组织机构。可以看到handleMessage方法定义了每个消息格式采用不同的消息处理方法,而这些方法该类并**没有实现**,而是留给了子类去实现。
27 1
【Spring源码】WebSocket做推送动作的底层实例是谁
|
2月前
|
XML 存储 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache管理器的实战开发指南(修正篇)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache管理器的实战开发指南(修正篇)
35 0
|
1天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
8 2
|
4天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
13天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
17天前
|
存储 缓存 Java
【spring】06 循环依赖的分析与解决
【spring】06 循环依赖的分析与解决
9 1