spring中控制事务的方式有两种:编程式事务和声明式事务,今天我以两种事务出发,对spring中实现事务的@EnableTransactionManagement和@Transaction两个注解的底层原理进行讨论。
一、编程式事务
什么是编程式事务?
硬编码的方式实现事务,在代码中手动开始、提交和回滚事务
编程式事务实现思想是什么?
- 配置PlatformTransactionManager事务管理器去控制事务
- 配置TransactionDefinition设置事务属性
- 配置TransactionTemplate控制事务
步骤和原理
1、定义数据源
2、定义一个PlatformTransactionManager 事务管理器,指定数据源。控制事务的操作(开始、提交、回滚)
3、定义TransactionDefinition 事务属性,可以配置事务属性信息
4、开启事务操作。通过调用getTransaction方法
补充:ThreadLocal中存储了datasource和connection的映射
这样当我们开启事务的时候会创建一个数据库连接,通过ThreadLocal保证线程的同步
5、执行业务操作
6、commit提交/rollback回滚事务
优缺点
优点:
以在代码中精确地控制事务的起始点、提交点和回滚点,实现更细粒度的事务管理。
缺点:
- 代码侵入性:编程式事务管理会将事务管理逻辑直接嵌入到业务代码中,增加了代码的复杂度和维护成本,使得业务逻辑与事务管理耦合在一起。
- 重复性工作:在多个业务方法中可能需要重复编写事务管理逻辑,增加了代码冗余和维护工作量。
二、声明式事务
什么是声明式事务?
通过配置的方式去管理事务,可以是xml配置文件,也可以使用spring提供的@Transactional注解
声明式事务实现思想是什么?
- 添加@EnableTransactionManagement注解
- 添加@Transactional注解
步骤和原理
1、启用事务管理功能-配置类上加上@EnableTransactionManagement注解
2、定义事务管理器
3、需要开启事务的目标接口/类/方法上添加@Transaction注解
4、执行业务逻辑
5、启动spring容器,获取bean执行业务逻辑
优缺点
优点:
- 与业务逻辑分离:声明式事务管理将事务管理逻辑从业务代码中分离出来,使得业务逻辑更清晰,降低了代码的耦合性。
- 配置简单:通过注解或XML配置,可以简单地定义事务的传播行为、隔离级别等属性,而无需在每个业务方法中编写重复的事务管理代码。
- 易于维护:由于事务管理逻辑集中在配置中,易于维护和修改,提高了代码的可读性和可维护性。
- 提高一致性:声明式事务管理可以确保在所有业务方法中都应用相同的事务管理策略,提高了事务管理的一致性。
缺点:
- 灵活性有限:声明式事务管理的灵活性相对较低,无法在运行时动态地改变事务管理策略,有一定的局限性。
- RPC远程调用成功,但是本地事务回滚了,RPC调用无法回滚。并且事务中有远程调用,会拉长整个事务,导致本地事务的数据库连接一致被占用,最后可能会导致数据库连接池耗尽
在阿里巴巴的开发手册中也明确标出,我们用@Transactional注解的时候要谨慎,大家在业务场景中要谨慎使用哦!
上面我已经对spring实现事务的两种方式分别进行了说明,下面我们看看它的源码,解开事务这个神秘面纱!
三、源码分析
@EnableTransactionManagement
作用是什么?
开启spring自动管理事务。在spring容器启动的时候,会拦截所有bean的创建,判断当前bean中有没有用@Transaction注解,是不是需要让spring管理事务
判断规则:
- public方法上有没有用@Transaction注解
- 和bean的类相关的类/接口上有没有用@Transaction注解
当满足规则之后会通过aop的方式创建代理,并且在代理中添加一个TransactionInterceptor拦截器
注意:@Transaction注解在代理对象被创建并且方法被调用时生效
@Transactional注解生效,必须确保Spring能够为目标类创建代理对象,并且方法通过代理对象调用
TransactionInterceptor拦截器的作用是什么?
拦截@Transaction方法,在方法前后添加事务额外逻辑
如果代理中还有其他拦截器,拦截器的顺序如何指定呢?
通过order()方法修改事务拦截器的执行顺序
注意:默认值是 LOWEST_PRECEDENCE = Integer.MAX_VALUE,拦截器的执行顺序是order升序
int order() default Ordered.LOWEST_PRECEDENCE;
@Import(TransactionManagementConfigurationSelector.class)在这里的作用是什么?
@Import的作用是批量导入需要注册的类,完成bean的注册
那@Import导入了哪些类?
通过点进去TransactionManagementConfigurationSelector我们发现,里面有一个selectImports方法,这个方法的返回值是一个字符串数组,返回的字符串数组如果是正常的全限定类名才会被容器识别
通过查看selectImports方法源码,我们发现return了两个类-AutoProxyRegistrar、ProxyTransactionManageMentConfiguration,也就是说Import导入了这两个类
- AutoProxyRegistrar:启用spring aop功能,创建代理
- ProxyTransactionManageMentConfiguration:在aop中添加事务拦截器
①、AutoProxyRegistrar
点进源码,我们看registerBeanDefinitions这个方法,看过spring容器启动流程的小伙伴会发现,其中流程就会调用这个方法,AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)作用是向spring容器注册一个自动代理创建器,从而启用AOP代理的功能
在方法内部会导入InfrastructureAdvisorAutoProxyCreator类,给spring 容器中注册了一个后置处理器-BeanPostProcessor,拦截所有bean的创建并将符合规则的bean创建代理,对类进行增强
在生命周期阶段,创建bean的时候只有需要增强,就会调用BeanPostProcessor的after进行增强
②、ProxyTransactionManageMentConfiguration
配置类,一般是提供加了@Bean的一些方法。注册了一个事务拦截器TransactionInterceptor
总:通过对上面两个导入的类的源码分析我们明确了他们的作用,我们使用@Transaction标注的bean会通过AutoProxyRegistrar去启用aop功能,通过ProxyTransactionManagementConfiguration在aop中添加事务拦截器从而实现事务管理