【SEATA源码分析】spring 模块源码解析

一. 导读

spring是java开发必备框架,类一般都是通过spring进行管理。那spring之外的框架,尤其是想使用spring代理的类的框架,如何借助spring的扩展点来操作bean?
这篇文章主要分析 Seata 如何借助 spring扩展点 对代理的bean进行操作,生成Seata想要的数据库代理类和对 被全局事务注解的bean 织入不同事务模式对应的 advisor 实现类。

二. Spring模块主要类解析

在这里插入图片描述
这儿是spring模块的目录,类的主要功能:
@GlobalTransactional :注解在业务方法上用来开启全局事务,可以自定义超时时间、全局事务的名字、回滚时调用的类等。
@GlobalLock :声明事务仅执行在本地RM中,但是本次事务确保在更新状态下的操作记录不会被其他全局事务操作。即将本地事务的执行纳入seata分布式事务的管理,一起竞争全局锁,保证全局事务在执行的时候,本地业务不可以操作全局事务中的记录。
GlobalTransactionalInterceptor: 实现了spring的 MethondInterceptor,声明一个环绕通知。拦截器主要就是判断是否有上面两个注解,并调用相应处理方法。
MethodDesc: 持有方法和方法上全局事务注解,model类。
TccActionInterceptor: 是 TCC 模式下独有的方法拦截器,也实现了 spring 的MethondInterceptor

三. 环绕通知(拦截器)分析

目前的seata版本有两种 Advisor,一个是GlobalTransactionalInterceptor,一个是TccActionInterceptor
GlobalTranscationInterceptor是在scanner检测到不是TCC模式下的bean,并且方法上有@GlobalTransactional注解或者@GlobalLock注解才织入的。
TccActionInterceptor是当scanner检测到bean的父类接口中有@LocalTCC注解,才织入的。

GlobalTransactionalInterceptor 拦截器有三个成员变量:TransactionnalTemplateGlobalLockTemplateFailureHandler,分别处理有全局事务注解的方法、和全局锁注解的方法和失败的情况。
在这里插入图片描述
拦截器invoke()方法主要逻辑是根据注解的类型找到对应的模板进行执行,用到了模板模式,具体模板的逻辑其他模块进行解析。

四. 全局事务扫描类分析

GlobalTransactionalScanner中,通过利用spring的扩展点,对bean进行自己想要的处理。

这个类继承了 AbstractAutoProxyCreator并实现了三个类InitializingBeanApplicationContextAwareDisposableBean

实现的类主要是为了获取spring的上下文 ApplicationContext 和在实例化完成时进行 TM 、RM的初始化,并注册销毁时调用的钩子,比较简单。这里有一个可以优化的地方,就是当项目中没有扫描到存在@GlobalTransactional注解的方法时,可以不用进行TM的初始化。

重点在继承AbstractAutoProxyCreate,继承这个类,获得了两次操作bean的机会。
第一次通过使用 postProcessAfterInitialization(),在bean被创建后,属性注入之前,对bean进行处理。
在这里插入图片描述
这里主要就是对 bean类型为为DataSource子类的时候,进行bean的代理,把原有的DataSource对象换成自己想要的DataSourceProxy对象。这里可能会被重复调用,所以多了一个判断,如果被代理过了,则交给父类处理。

wrapIfNecessary()的调用时机是在postProcessAfterInitialization()之后
在这里插入图片描述
主要就是获取bean的类型,根据bean的类型来实例化对应的adsivor。

这里有一个问题,被远程调用的方法没有注解,如何纳入全局事务?
猜测:在进行远程调用的时候,带上了事务id,远程业务模块拦截器通过事务id生成本地事务。答案要在后续的源码解析中获得,这里只是猜测。

在这里插入图片描述
这里当不是aop代理的bean时,手动调用父类方法,父类会调用 getAdvicesAndAdvisorsForBean()来获取上面实例化好的interceptor。如果是aop代理,就把实例化好的interceptor拦截器增加到Advisor[]数组中,放置在第一位。
spring在执行完 wrapIfNecessery()后,会继续进行aop代理类的实例化。

五. 总结

Seata中的bean有三种:normalTccBean、localTccBean(被@LocalTcc注解)、normalBean(被@GlobalTransactional或者@GlobalLock注解),其中normalTccBean是不会被 wrap 的,其他两个会被代理,通过wrapIfNecessery(),织入advisor。

更多好文请关注微信公众号:我手一杯
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值