Spring事务专题(五)聊聊Spring事务到底是如何实现的
前言
本专题大纲:
本文为本专题倒数第二篇文章。
在上篇文章中我们一起学习了Spring中的事务抽象机制以及动手模拟了一下Spring中的事务管理机制,那么本文我们就通过源码来分析一下Spring中的事务管理到底是如何实现的,本文将选用Spring5.2.x
版本。
从@EnableTransactionManagement开始
Spring事务管理的入口就是@EnableTransactionManagement
注解,所以我们直接从这个注解入手,其源码如下:
public @interface EnableTransactionManagement {
// 是否使用cglib代理,默认是jdk代理
boolean proxyTargetClass() default false;
// 使用哪种代理模式,Spring AOP还是AspectJ
AdviceMode mode() default AdviceMode.PROXY;
// 为了完成事务管理,会向容器中添加通知
// 这个order属性代表了通知的执行优先级
// 默认是最低优先级
int order() default Ordered.LOWEST_PRECEDENCE;
}
需要注意的是,@EnableTransactionManagement
的proxyTargetClass
会影响Spring中所有通过自动代理生成的对象。如果将proxyTargetClass
设置为true,那么意味通过@EnableAspectJAutoProxy
所生成的代理对象也会使用cglib进行代理。关于@EnableTransactionManagement
跟@EnableAspectJAutoProxy
混用时的一些问题等我们在对@EnableTransactionManagement
有一定了解后再专门做一个比较,现在我们先来看看这个注解到底在做了什么?
从上图中可以看出这个注解做的就是向容器中注册了AutoProxyRegistrar
跟一个ProxyTransactionManagementConfiguration
(这里就不考虑AspectJ了,我们平常都是使用SpringAOP),
AutoProxyRegistrar
用于开启自动代理,其源码如下:
AutoProxyRegistrar分析
这个类实现了ImportBeanDefinitionRegistrar
,它的作用是向容器中注册别的BeanDefinition
,我们直接关注它的registerBeanDefinitions
方法即可
// AnnotationMetadata,代表的是AutoProxyRegistrar的导入类的元信息
// 既包含了类元信息,也包含了注解元信息
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
// 获取@EnableTransactionManagement所在配置类上的注解元信息
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
// 遍历注解
for (String annType : annTypes) {
// 可以理解为将注解中的属性转换成一个map
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {
continue;
}
// 直接从map中获取对应的属性
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
// mode,代理模型,一般都是SpringAOP
// proxyTargetClass,是否使用cglib代理
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
// 注解中存在这两个属性,并且属性类型符合要求,表示找到了合适的注解
candidateFound = true;
// 实际上会往容器中注册一个InfrastructureAdvisorAutoProxyCreator
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
// ......
}
@EnableTransactionManagement跟@EnableAspectJAutoProxy
如果对AOP比较了解的话,那么应该知道@EnableAspectJAutoProxy
注解也向容器中注册了一个能实现自动代理的bd,那么当@EnableAspectJAutoProxy
跟@EnableTransactionManagement
同时使用会有什么问题吗?答案大家肯定知道,不会有问题,那么为什么呢?我们查看源码会发现,@EnableAspectJAutoProxy
最终调用的是
AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
,其源码如下
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
@EnableTransactionManagement
最终调用的是,AopConfigUtils#registerAutoProxyCreatorIfNecessary
,其源码如下
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
它们最终都会调用registerOrEscalateApcAsRequired
方法,只不过传入的参数不一样而已,一个是AnnotationAwareAspectJAutoProxyCreator
,另一个是InfrastructureAdvisorAutoProxyCreator
。
registerOrEscalateApcAsRequired
源码如下:
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 当前已经注册到容器中的Bean的优先级
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
// 当前准备注册到容器中的Bean的优先级
int requiredPriority = findPriorityForClass(cls);
// 谁的优先级大就注册谁,AnnotationAwareAspectJAutoProxyCreator是最大的
// 所以AnnotationAwareAspectJAutoProxyCreator会覆盖别的Bean
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 注册bd
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
InfrastructureAdvisorAutoProxyCreator
跟AnnotationAwareAspectJAutoProxyCreator
的优先级是如何定义的呢?我们来看看AopConfigUtils
这个类中的一个静态代码块
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
实际上它们的优先级就是在APC_PRIORITY_LIST
这个集合中的下标,下标越大优先级越高,所以AnnotationAwareAspectJAutoProxyCreator
的优先级最高,所以AnnotationAwareAspectJAutoProxyCreator
会覆盖InfrastructureAdvisorAutoProxyCreator
,那么这种覆盖会不会造成问题呢?答案肯定是不会的,因为你用了这么久了也没出过问题嘛~那么再思考一个问题,为什么不会出现问题呢?这是因为InfrastructureAdvisorAutoProxyCreator
只会使用容器内部定义的Advisor
,但是AnnotationAwareAspectJAutoProxyCreator
会使用所有实现了Advisor
接口的通知,也就是说AnnotationAwareAspectJAutoProxyCreator
的作用范围大于InfrastructureAdvisorAutoProxyCreator
,因此这种覆盖是没有问题的。限于篇幅原因这个问题我不做详细解答了,有兴趣的同学可以看下两个类的源码。
@EnableTransactionManagement
除了注册了一个AutoProxyRegistrar
外,还向容器中注册了一个ProxyTransactionManagementConfiguration
。
那么这个ProxyTransactionManagementConfiguration
有什么作用呢?
❝ 如果大家对我文章的风格有一些了解的话就会知道,分析一个类一般有两个切入点
- 它的继承关系
- 它提供的API
大家自己在阅读源码时也可以参考这种思路,分析一个类的继承关系可以让我们了解它从抽象到实现的过程,即使不去细看API也能知道它的大体作用。仅仅知道它的大致作用是不够的,为了更好了解它的细节我们就需要进一步去探索它的具体实现,也就是它提供的API。这算是我看了这么就源码的一点心得,正好想到了所以在这里分享下,之后会专门写一篇源码心得的文章
❞
ProxyTransactionManagementConfiguration分析
继承关系
这个类的继承关系还是很简单的,只有一个父类AbstractTransactionManagementConfiguration
AbstractTransactionManagementConfiguration
源码如下:
@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
@Nullable
protected AnnotationAttributes enableTx;
@Nullable
protected TransactionManager txManager;
// 这个方法就是获取@EnableTransactionManagement的属性
// importMetadata:就是@EnableTransactionManagement这个注解所在类的元信息
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
// 将EnableTransactionManagement注解中的属性对存入到map中
// AnnotationAttributes实际上就是个map
this.enableTx = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
// 这里可以看到,限定了导入的注解必须使用@EnableTransactionManagement
if (this.enableTx == null) {
throw new IllegalArgumentException(
"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
}
}
// 我们可以配置TransactionManagementConfigurer
// 通过TransactionManagementConfigurer向容器中注册一个事务管理器
// 一般不会这么使用,更多的是通过@Bean的方式直接注册
@Autowired(required = false)
void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
// .....
TransactionManagementConfigurer configurer = configurers.iterator().next();
this.txManager = configurer.annotationDrivenTransactionManager();
}
// 向容器中注册一个TransactionalEventListenerFactory
// 这个类用于处理@TransactionalEventListener注解
// 可以实现对事件的监听,并且在事务的特定阶段对事件进行处理
@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
return new TransactionalEventListenerFactory();
}
}
TransactionalEventListenerFactory
上面的代码中大家可能比较不熟悉的就是TransactionalEventListenerFactory
,这个类主要是用来处理@TransactionalEventListener
注解的,我们来看一个实际使用的例子
@Component
public class DmzListener {
// 添加一个监听器
// phase = TransactionPhase.AFTER_COMMIT意味着这个方法在事务提交后执行
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void listen(DmzTransactionEvent transactionEvent){
System.out.println("事务已提交");
}
}
// 定义一个事件
public class DmzTransactionEvent extends ApplicationEvent {
public DmzTransactionEvent(Object source) {
super(source);
}
}
@Component
public class DmzService {
@Autowired
ApplicationContext applicationContext;
// 一个需要进行事务管理的方法
@Transactional
public void invokeWithTransaction() {
// 发布一事件
applicationContext.publishEvent(new DmzTransactionEvent(this));
// 以一条sout语句提代sql执行过程
System.out.println("sql invoked");
}
}
// 测试方法
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(Config.class);
DmzService dmzService = ac.getBean(DmzService.class);
dmzService.invokeWithTransaction();
}
}
// 最后程序会按顺序输出
// sql invoked
// 事务已提交
通过上面的例子我们可以看到,虽然我们在invokeWithTransaction
方法中一开始就发布了一个事件,但是监听事件的方法却是在invokeWithTransaction
才执行的,正常事件的监听是同步的,假设我们将上述例子中的@TransactionalEventListener
注解替换成为@EventListener
注解,如下:
@Component
public class DmzListener {
// 添加一个监听器
@EventListener
public void listen(DmzTransactionEvent transactionEvent){
System.out.println("事务已提交");
}
}
这个时候程序的输出就会是
// 事务已提交
// sql invoked
那么@TransactionalEventListener
注解是实现这种「看似异步」(实际上并不是)的监听方式的呢?
大家按照上面这个调用链可以找到这么一段代码
通过上面的代码,我们可以发现最终会调用到TransactionalEventListenerFactory
的createApplicationListener
方法,通过这个方法创建一个事件监听器然后添加到容器中,createApplicationListener
方法源码如下:
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);
}
就是创建了一个ApplicationListenerMethodTransactionalAdapter
,这个类本身就是一个事件监听器(实现了ApplicationListener
接口)。我们直接关注它的事件监听方法,也就是onApplicationEvent
方法,其源码如下:
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 激活了同步,并且真实存在事务
if (TransactionSynchronizationManager.isSynchronizationActive() &&
TransactionSynchronizationManager.isActualTransactionActive()) {
// 实际上是依赖事务的同步机制实现的事件监听
TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event);
TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);
}
// 在没有开启事务的情况下是否处理事件
else if (this.annotation.fallbackExecution()) {
// ....
// 如果注解中的fallbackExecution为true,意味着没有事务开启的话
// 也会执行监听逻辑
processEvent(event);
}
else {
// ....
}
}
到这一步逻辑已经清楚了,@TransactionalEventListener
所标注的方法在容器启动时被解析成了一个ApplicationListenerMethodTransactionalAdapter
,这个类本身就是一个事件监听器,当容器中的组件发布了一个事件后,如果事件匹配,会进入它的onApplicationEvent
方法,这个方法并没有直接执行我们所定义的监听逻辑,而是给当前事务注册了一个同步的行为,当事务到达某一个阶段时,这个行为会被触发。通过这种方式,实现一种「伪异步」。实际上注册到事务的的同步就是TransactionSynchronizationEventAdapter
,这个类的源码非常简单,这里就单独取它一个方法看下
// 这个方法会在事务提交前执行
public void beforeCommit(boolean readOnly) {
// 在执行时会先判断在@TransactionalEventListener注解中定义的phase是不是BEFORE_COMMIT
// 如果不是的话,什么事情都不做
if (this.phase == TransactionPhase.BEFORE_COMMIT) {
processEvent();
}
}
别看上面这么多内容,到目前为止我们还是只对ProxyTransactionManagementConfiguration
的父类做了介绍,接下来我们就来看看ProxyTransactionManagementConfiguration
自身做了什么事情。