spring事务源码之构建事务代理对象

spring 通过 aop 来实现事务功能,所以操作时使用的也是代理对象。有兴趣的可以先看看我前面那篇文章《spring aop源码详解》。

本文主要讲解事务的代理对象的创建,以及事务拦截器 TransactionInterceptor 创建的整个流程的源码。

平时开发中,正常都使用 @Transactional 注解来实现事务功能。看一个简单的例子,我在 pay 方法上加了 @Transactional 注解。下面讲源码会以这个为例。

@Transactional
@Override
public void pay(Payment payment) {
	Item item = itemService.getById(payment.getItemId());
	payment.setUnitPrice(item.getUnitPrice());
	payment.setPayment(item.getUnitPrice() * payment.getAmount());
	payment.setPayTime(new Date());
	// 创建支付记录
	paymentMapper.insert(payment);
	// 扣减库存
	itemService.deductStock(payment.getItemId(), payment.getAmount());
	System.out.println( 1 / 0 );
    log.info("pay success!");
}

这是一个简易的接口,先保存支付记录,然后扣减库存。但是在扣减库存后,有一个错误的计算,所以接口会报错,前面添加的支付记录和扣减的库存会被回滚。

首先编写测试类并 debug 看下效果

@Test
void pay() {
	Payment payment = new Payment();
	payment.setUserId(1);
	payment.setItemId(1);
	payment.setAmount(2);
	paymentService.pay(payment);
}

F7 进入 pay 方法,这跟前面讲过的 aop 一样,会进入代理对象中,但是比普通代理对象多了一个拦截器,那就是 TransactionInterceptor,因为我在方法上加了 @Transactional 注解开启了事务功能。如果不加这注解,是不会有这个拦截器的。

这个 TransactionInterceptor 是由 BeanFactoryTransactionAttributeSourceAdvisor 转化而来的。这个是适配器模式,在我前面的文章中讲过,如何将 advisor 转化为 Interceptor。

所以我们首先要看的是,这个代理对象是在什么时候创建的,然后这个 advisor 是在哪里设置进去的。

在上一篇文章《spring aop源码详解》中讲过,创建代理对象,是在 bean 的初始化方法中进行的,在 bean 的初始化完成后,会调用 BeanPostProcessor 的后置方法

BeanPostProcessor 中有个 AnnotationAwareAspectJAutoProxyCreator,由它来创建代理对象。进入它的 postProcessAfterInitialization 方法,一步步往下看

一直进到 findEligibleAdvisors 方法中,这个方法用来寻找合格的通知器。先看看它里面能获取的值,确实有我们想看到的 BeanFactoryTransactionAttributeSourceAdvisor。

继续往下看,调用层数有点多

发现缓存中已经有了,说明这个地方之前已经调用过了。其实也正常,我看的是 paymentServiceImpl 这个 bean,已经排在很后面了。其他 bean 初始化时,已经调用过这个方法了。

所以重新 debug,在这个地方打上断点。可以看到,目前这个 cachedAdvisorBeanName 还是空的。而且 beanNamesForTypeIncludingAncestors 方法确实会返回我们希望得到的 beanName。

进入方法内部,里面根据 Advisor 类型,从 ioc 容器 DefaultListableBeanFactory 中获取 beanName。获得的 beanName 是 “org.springframework.transaction.config.internalTransactionAdvisor”,而前面也看到了,这个 beanName 对应的 bean,就是 BeanFactoryTransactionAttributeSourceAdvisor!

那么问题又来了,这个 bean 是怎么进入到 ioc 容器中的呢?哎,看源码就是累,一层又一层。

要知道这个原因,就要继续到前面去寻找。在 loadBeanDefinitions 方法加载 BeanDefinition 中遍历查找,看根据哪个 ConfigurationClass 进行 loadBeanDefinitions 时,把这个 internalTransactionAdvisor 放入 beanDefinitionMap,发现了是 ProxyTransactionManagementConfiguration 类

进到这个类里看下,里面确实定义了一个 @Bean,返回 BeanFactoryTransactionAttributeSourceAdvisor 对象。

前面的文章《spring ioc源码讲解之加载BeanDefinition》中讲过,当加载一个类时,如果这个类中定义了 @Bean。那么同时会加载 @Bean 注解对应的 bean。此处就是 BeanFactoryTransactionAttributeSourceAdvisor。

那么这边又来了个问题,ProxyTransactionManagementConfiguration 又是在哪里放进 ConfigurationClass 的呢?

在我另一篇文章《springboot自动装配源码详解》中,讲到了 springboot 在启动时, 从各个包中的 META-INF/spring.factories 中获取配置类的详细过程。

这里获取了32个自动配置类的全限定名,并将他们封装为 ConfigurationClass。所以,ProxyTransactionManagementConfiguration 也是在这个地方放进去的。那么是在解析哪个自动配置类时,将它放进去的呢,猜一下,应该是 TransactionAutoConfiguration,因为跟事务相关的,就属它了。

进入 TransactionAutoConfiguration 的那层循环,进入 processImports 方法看下。

这个方法的 importCandidates 参数是 TransactionAutoConfiguration 类型。进去看看做了些什么。

进入 processConfigurationClass 方法,首先将 TransactionAutoConfiguration 转化为 SourceClass,然后调用 doProcessConfigurationClass 方法进行真正的处理。

在这个方法里面,当判断此配置类被包含 @Component 注解的话,会去处理它内部定义的类。很显然 TransactionAutoConfiguration 是有 @Configuration 注解的,它包含了 @Component,所以开始处理它内部定义的类。

它里面有两个类,我们要找的是这个 EnableTransactionManagementConfiguration

其实根据它上面的注解 @EnableTransactionManagement,就可以知道是它,因为这个注解导入的 TransactionManagementConfigurationSelector 类,然后它里面的 selectImports 方法中,返回的就是我们要找的 ProxyTransactionManagementConfiguration 类。不过还是需要 debug 代码佐证一下。

继续往下走,进入 EnableTransactionManagementConfiguration 对应的 doProcessConfigurationClass 方法。

它里面还有两个类!

继续进入 CglibAutoProxyConfiguration 对应的 doProcessConfigurationClass 方法,找到调用 processImports 方法的地方,可以看到它导入了一个类 TransactionManagementConfigurationSelector。 

进入 processImports,看到它调用了 TransactionManagementConfigurationSelector 的 selectImports 方法!

进去看看

好了,总算看到返回了我们要找的 ProxyTransactionManagementConfiguration 了! 

写了这么久,只是解释了文章开头看到的 TransactionInterceptor 是怎么来的!

总结一下整个流程吧:

一、springboot 工程启动,通过自动装配机制,导入了 TransactionAutoConfiguration 类,TransactionAutoConfiguration 类中定义了一个静态类 EnableTransactionManagementConfiguration。EnableTransactionManagementConfiguration 类中还有一个静态类 CglibAutoProxyConfiguration,CglibAutoProxyConfiguration 类上有个注解 @EnableTransactionManagement,注解 @EnableTransactionManagement 上导入了一个类 TransactionManagementConfigurationSelector,TransactionManagementConfigurationSelector 中有个方法 selectImports,该方法中如果 adviceMode 为 “PROXY” 的话,返回 ProxyTransactionManagementConfiguration 配置类。

二、然后,在对 TransactionAutoConfiguration 这个 bean 进行解析(parser.parse 方法)时,会一步步将它里面的静态类也解析出来,放到 configurationClasses 集合中缓存起来。

三、之后真正加载 beanDefinition(reader.loadBeanDefinitions)操作,在对 ProxyTransactionManagementConfiguration 进行加载时,发现它里面有通过 @Bean 定义了一个 transactionAdvisor,实际上返回的是一个 BeanFactoryTransactionAttributeSourceAdvisor。将它加载到 beanDefinitionMap 中。

四、进行 bean 的实例化和初始化操作,最终它的 beanName 为 “org.springframework.transaction.config.internalTransactionAdvisor”,值是 BeanFactoryTransactionAttributeSourceAdvisor。

五、初始化完成后,进行 BeanPostProcessor 的后置操作 postProcessAfterInitialization。在这个里面,对 paymentServiceImpl 创建代理对象。创建代理对象的过程中,会通过 findEligibleAdvisors 方法,查询合适的 Advisor,其中就包括了 BeanFactoryTransactionAttributeSourceAdvisor。将它跟这个代理对象关联。

六、调用方法时,实际上使用的是代理对象。首先会获取到通知器,也就是 BeanFactoryTransactionAttributeSourceAdvisor,然后将它转化成 TransactionInterceptor 拦截器。

本来想一篇文章写完事务源码的,不过光获取 advisor 就花了这么多篇幅,所以先写到这吧,其他的放到下一篇文章再写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值