【微服务37】分布式事务Seata源码解析五:@GlobalTransactional如何开启全局事务

简介: 【微服务37】分布式事务Seata源码解析五:@GlobalTransactional如何开启全局事务

@[TOC]

一、前言

至此,seata系列的内容包括:

  1. can not get cluster name in registry config ‘service.vgroupMapping.xx‘, please make sure registry问题解决
  2. Seata Failed to get available servers: endpoint format should like ip:port 报错原因/解决方案汇总版(看完本文必解决问题)
  3. Seata json decode exception, Cannot construct instance of java.time.LocalDateTime报错原因/解决方案最全汇总版
  4. 【微服务 31】超细的Spring Cloud 整合Seata实现分布式事务(排坑版)
  5. 【微服务 32】Spring Cloud整合Seata、Nacos实现分布式事务案例(巨细排坑版)【云原生】
  6. 【微服务33】分布式事务Seata源码解析一:在IDEA中启动Seata Server
  7. 【微服务34】分布式事务Seata源码解析二:Seata Server启动时都做了什么【云原生】
  8. 【微服务35】分布式事务Seata源码解析三:从Spring Boot特性来看Seata Client 启动时都做了什么
  9. 【微服务36】分布式事务Seata源码解析四:图解Seata Client 如何与Seata Server建立连接、通信

本文接着Seata使用@GlobalTransactional是如何开启全局事务的?

PS:前文中搭建的Seata案例,seata的版本为1.3.0,而本文开始的源码分析将基于当前(2022年8月)最新的版本1.5.2进行源码解析。

二、@GlobalTransactional

我们知道可以将@GlobalTransactional注解标注在类或方法上 开启全局事务,下面来看一下@GlobalTransactional是如何开启的全局事务?

【微服务35】分布式事务Seata源码解析三:从Spring Boot特性来看Seata Client 启动时都做了什么一文中,我们知道了SpringBoot启动过程中会自动装配GlobalTransactionScanner类;

1、GlobalTransactionScanner类(BPP)

先看GlobalTransactionScanner类的继承关系:

在这里插入图片描述

GlobalTransactionScanner类继承了AbstractAutoProxyCreatorAbstractAutoProxyCreator类又实现了BeanPostProcessor接口;因此GlobalTransactionScanner类也是BPP(BeanPostProcessor)

下面简单看一下AbstractAutoProxyCreator类;

1)AbstractAutoProxyCreator(自动创建动态代理)

在这里插入图片描述

AbstractAutoProxyCreator是Spring AOP中的一个抽象类,其主要功能是自动创建动态代理;因为其实现了BeanPostProcessor接口,所以在类加载到Spring容器之前,会进入到其wrapIfNecessary()方法对Bean进行代理包装,后续调用Bean之将委托给指定的拦截器。

另外其getAdvicesAndAdvisorsForBean()方法用于给子类实现,由子类决定一个Bean是否需要被代理(是否存在切面);并且它还可以返回只应用于特定Bean实例的附加拦截器

2)BeanPostProcessor(对Bean进行修改的入口)

BeanPostProcessor是Bean的后置处理器,可以通过实现其 并 覆写其postProcessBeforeInitialization()postProcessAfterInitialization() 方法在Bean初始化前后对其进行修改;

AbstractAutoProxyCreator正是通过覆写BeanPostProcessorpostProcessAfterInitialization() 创建并返回Bean的代理对象;

在这里插入图片描述

下面从SpringBoot启动流程来看针对标注了@GlobalTransactional的类 或 类中包含标注了@GlobalTransactional方法的类 创建动态代理的入口。

3)从SpringBoot启动流程来看入口

TradeService类为例:

package com.saint.trade.service;

import com.saint.trade.feign.OrderFeignClient;
import com.saint.trade.feign.StockFeignClient;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * @author Saint
 */
@Service
@RequiredArgsConstructor
public class TradeService {

    private final StockFeignClient stockFeignClient;
    private final OrderFeignClient orderFeignClient;

    /**
     * 减库存,下订单
     *
     * @param userId
     * @param commodityCode
     * @param orderCount
     */
    @GlobalTransactional
    public void purchase(String userId, String commodityCode, int orderCount) {
        stockFeignClient.deduct(commodityCode, orderCount);

        orderFeignClient.create(userId, commodityCode, orderCount);
    }
    
    public void test() {
        System.out.println("hhahaha");
    }
}

TradeService类被@Component衍生注解@Service标注,TradeService类又在SpringBoot扫描路径中,因此SpringBoot启动时会扫描到TradeService类;

TradeService类中包含两个方法:purchase()test(),其中purchase()方法被@GlobalTransactional注解标注。

下面来看创建Bean时设计到BeanPostProcessor的代码片段:

AbstractBeanFactory抽象Bean工厂的实现类AbstractAutowireCapableBeanFactoryinitializeBean()方法是初始化Bean的入口:

在这里插入图片描述
在Bean初始化之后会调用BeanPostProcessorpostProcessAfterInitialization() 创建并返回Bean的代理对象;整体线程栈帧信息如下:

在这里插入图片描述

在这里插入图片描述

最终进入到GlobalTransactionScanner覆写AbstractAutoProxyCreator抽象类的wrapIfNecessary()方法创建并返回代理对象(如果需要创建动态代理的话)。

4)是否 / 如何生成动态代理对象

从上面我们知道了GlobalTransactionScanner类的wrapIfNecessary()是创建动态代理的入口;这里接着来看wrapIfNecessary()方法如何判断是否Bean是否需要生成动态代理?

// 对于扫描到的@GlobalTransactional注解, bean和beanName
// 判断类 或 类的某一个方法是否被@GlobalTransactional注解标注,进而决定当前Class是否需要创建动态代理;存在则创建。
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // do checkers,做一些检查,不用花过多精力关注
    if (!doCheckers(bean, beanName)) {
        return bean;
    }

    try {
        synchronized (PROXYED_SET) {
            if (PROXYED_SET.contains(beanName)) {
                return bean;
            }
            interceptor = null;
            //check TCC proxy TCC的动态代理
            if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                // init tcc fence clean task if enable useTccFence
                TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
                //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
                interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                        (ConfigurationChangeListener) interceptor);
            } else {
                // 先获取目标Class的接口
                Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);

                // existsAnnotation()表示类或类方法是否有被@GlobalTransactional注解标注,进而决定类是否需要被动态代理
                if (!existsAnnotation(new Class[]{serviceInterface})
                        && !existsAnnotation(interfacesIfJdk)) {
                    return bean;
                }

                if (globalTransactionalInterceptor == null) {
                    // 构建一个全局拦截器
                    globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                    ConfigurationCache.addConfigListener(
                            ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                            (ConfigurationChangeListener) globalTransactionalInterceptor);
                }
                interceptor = globalTransactionalInterceptor;
            }

            LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
            // 如果当前Bean没有被AOP代理
            if (!AopUtils.isAopProxy(bean)) {
                // 基于Spring AOP的AutoProxyCreator对当前Class创建全局事务动态动态代理类
                bean = super.wrapIfNecessary(bean, beanName, cacheKey);
            } else {
                AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                int pos;
                for (Advisor avr : advisor) {
                    // Find the position based on the advisor's order, and add to advisors by pos
                    // 找到seata切面的位置
                    pos = findAddSeataAdvisorPosition(advised, avr);
                    advised.addAdvisor(pos, avr);
                }
            }
            PROXYED_SET.add(beanName);
            return bean;
        }
    } catch (Exception exx) {
        throw new RuntimeException(exx);
    }
}

第一步

1> 首先doCheckers()方法对Bean做一些检查,包括:Bean是否已经生成了代理类、Bean不允许生成代理类.....

在这里插入图片描述

第二步

2> 对已经创建了动态代理的Bean的Set集合PROXYED_SET加锁做同步操作,如果PROXYED_SET中存在当前Bean的代理对象,则直接返回。

在这里插入图片描述

第三步

3> 根据@TwoPhaseBusinessAction注解判断是否是TCC模式下的动态代理(默认是AT模式,即不是TCC模式);

在这里插入图片描述

第四步

4> 获取Bean的目标Class,再通过existsAnnotation()方法查看 类或类方法是否有被@GlobalTransactional注解标注,进而决定类是否需要被动态代理;

在这里插入图片描述

existsAnnotation()方法用于判断类是否需要被动态代理

在这里插入图片描述

existsAnnotation()方法判断类是否需要被动态代理时:

  1. 首先判断类上是否标注了@GlobalTransactional注解,如果标注了,则直接返回true,表示类需要被动态代理;
  2. 否者,接着利用反射获取类的所有public方法,只要存在一个方法被@GlobalTransactional@GlobalLock 注解标注,则表示当前类需要被动态代理;

PS:聪明的你肯定发现,当一个类中有多个方法并且类没有被@GlobalTransactional注解标注,但只有一个方法被@GlobalTransactional注解标注时,这里针对整个类生成了动态代理对象,那么调用没加@GlobalTransactional注解的方法也会进入到代理对象,会不会有问题呢? 继续往后看,拦截器GlobalTransactionalInterceptor中会对其进行处理。

当目标Class需要被动态代理时,则会初始化一个拦截器GlobalTransactionalInterceptor,用于拦截后面对目标Class的调用;

在这里插入图片描述

那么GlobalTransactionalInterceptor是如何被应用于目标Class上做拦截的?

第五步

5> 如果针对当前Bean的代理是JDK 或 CGLIB动态代理,则根据GlobalTransactionalInterceptor创建切面,并应用到Bean上;

在这里插入图片描述

在第四步时,我们对GlobalTransactionalInterceptor如何被应用于目标Class上做拦截持有疑问,在前面介绍AbstractAutoProxyCreator我们提到过:

AbstractAutoProxyCreator的 getAdvicesAndAdvisorsForBean()方法用于给子类实现,由子类决定一个Bean是否需要被代理(是否存在切面);并且它还可以返回 只应用于特定Bean实例的附加拦截器;>>

在这里插入图片描述

GlobalTransactionScanner覆写了getAdvicesAndAdvisorsForBean()方法,将上面初始化后的GlobalTransactionalInterceptor作为切面返回给AbstractAutoProxyCreator,供其创建动态代理类时使用;

在这里插入图片描述

创建完代理对象之后,将代理对象放入到GlobalTransactionScanner的动态代理Bean的Set集合PROXYED_SET,以快去获取Bean的代理对象 并 防止Bean代理对象的重复创建。最后将代理对象返回,创建Bean流程结束。

==至此,我们知道了所谓的@GlobalTransactional注解开启全局事务,实际就是针对类 或 类的方法上标注了@GlobalTransactional注解的类创建动态代理对象==

三、全局事务的执行(前戏)

上面我们知道了所谓的@GlobalTransactional注解开启全局事务,其实就是类 或 类的方法上标注了@GlobalTransactional注解的类创建动态代理对象。但是动态代理对象是针对类的;

当一个类中有多个方法并且类没有被@GlobalTransactional注解标注,但只有一个方法被@GlobalTransactional注解标注时,这里针对整个类生成了动态代理对象,当调用Bean时,拦截器GlobalTransactionalInterceptor会做进一步处理,保证只有加了@GlobalTransactional注解的方法才会开启全局事务。

先看GlobalTransactionalInterceptor类的继承图:

在这里插入图片描述

GlobalTransactionalInterceptor实现了MethodInterceptor接口,所以当每次执行添加了 GlobalTransactionalInterceptor拦截器的Bean的方法时,都会进入到GlobalTransactionalInterceptor类覆写MethodInterceptor接口的invoke()方法:

@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
    // method invocation是一次方法调用,一定是针对某个对象的方法调用;
    // methodInvocation.getThis()就是拿到当前方法所属的对象;
    // AopUtils.getTargetClass()获取到当前实例对象所对应的Class
    Class<?> targetClass =
            methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;

    // 通过反射获取到被调用目标Class的method方法
    Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);

    // 如果目标method不为空,并且方法的DeclaringClass不是Object
    if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
        // 通过BridgeMethodResolver寻找method的桥接方法
        final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
        // 获取目标方法的@GlobalTransactional注解
        final GlobalTransactional globalTransactionalAnnotation =
                getAnnotation(method, targetClass, GlobalTransactional.class);
        // 如果目标方法被@GlobalLock注解标注,获取到@GlobalLock注解内容
        final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
        // 如果禁用了全局事务 或 开启了事务降级检查并且降级检查次数大于等于降级检查允许的次数
        // 则localDisable等价于全局事务被禁用了
        boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);

        // 如果全局事务没有被禁用
        if (!localDisable) {
            // 全局事务注解不为空 或者 AOP切面全局事务核心配置不为空
            if (globalTransactionalAnnotation != null || this.aspectTransactional != null) {
                AspectTransactional transactional;
                if (globalTransactionalAnnotation != null) {
                    // 构建一个AOP切面全局事务核心配置,配置的数据从全局事务注解中取
                    transactional = new AspectTransactional(globalTransactionalAnnotation.timeoutMills(),
                            globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(),
                            globalTransactionalAnnotation.rollbackForClassName(),
                            globalTransactionalAnnotation.noRollbackFor(),
                            globalTransactionalAnnotation.noRollbackForClassName(),
                            globalTransactionalAnnotation.propagation(),
                            globalTransactionalAnnotation.lockRetryInterval(),
                            globalTransactionalAnnotation.lockRetryTimes());
                } else {
                    transactional = this.aspectTransactional;
                }
                // 真正处理全局事务的入口
                return handleGlobalTransaction(methodInvocation, transactional);
            } else if (globalLockAnnotation != null) {
                // 获取事务锁
                return handleGlobalLock(methodInvocation, globalLockAnnotation);
            }
        }
    }
    // 直接运行目标方法
    return methodInvocation.proceed();
}

假如我们调用TradeService类中没有标注@GlobalTransactional注解的test()方法;

在这里插入图片描述

invoke()方法中会再次判断 当前调用的bean的方法 或 方法所处的类上是否标注了@GlobalTransactional注解,如果没有标注,则执行运行目标方法;否则才会以全局事务的方式执行方法。

四、总结

所谓的@GlobalTransactional注解开启全局事务,实际就是针对类 或 类的方法上标注了@GlobalTransactional注解的类创建动态代理对象。

在调用相应Bean的时候,会进入到动态代理对象的拦截器GlobalTransactionalInterceptor

相关文章
|
6月前
|
存储 安全 Java
管理 Spring 微服务中的分布式会话
在微服务架构中,管理分布式会话是确保用户体验一致性和系统可扩展性的关键挑战。本文探讨了在 Spring 框架下实现分布式会话管理的多种方法,包括集中式会话存储和客户端会话存储(如 Cookie),并分析了它们的优缺点。同时,文章还涵盖了与分布式会话相关的安全考虑,如数据加密、令牌验证、安全 Cookie 政策以及服务间身份验证。此外,文中强调了分布式会话在提升系统可扩展性、增强可用性、实现数据一致性及优化资源利用方面的显著优势。通过合理选择会话管理策略,结合 Spring 提供的强大工具,开发人员可以在保证系统鲁棒性的同时,提供无缝的用户体验。
147 0
|
7月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1193 3
|
5月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
11月前
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
389 5
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
2929 1
|
10月前
|
安全 JavaScript 前端开发
HarmonyOS NEXT~HarmonyOS 语言仓颉:下一代分布式开发语言的技术解析与应用实践
HarmonyOS语言仓颉是华为专为HarmonyOS生态系统设计的新型编程语言,旨在解决分布式环境下的开发挑战。它以“编码创造”为理念,具备分布式原生、高性能与高效率、安全可靠三大核心特性。仓颉语言通过内置分布式能力简化跨设备开发,提供统一的编程模型和开发体验。文章从语言基础、关键特性、开发实践及未来展望四个方面剖析其技术优势,助力开发者掌握这一新兴工具,构建全场景分布式应用。
943 35
|
运维 监控 持续交付
微服务架构解析:跨越传统架构的技术革命
微服务架构(Microservices Architecture)是一种软件架构风格,它将一个大型的单体应用拆分为多个小而独立的服务,每个服务都可以独立开发、部署和扩展。
3560 38
微服务架构解析:跨越传统架构的技术革命
|
物联网 调度 vr&ar
鸿蒙HarmonyOS应用开发 |鸿蒙技术分享HarmonyOS Next 深度解析:分布式能力与跨设备协作实战
鸿蒙技术分享:HarmonyOS Next 深度解析 随着万物互联时代的到来,华为发布的 HarmonyOS Next 在技术架构和生态体验上实现了重大升级。本文从技术架构、生态优势和开发实践三方面深入探讨其特点,并通过跨设备笔记应用实战案例,展示其强大的分布式能力和多设备协作功能。核心亮点包括新一代微内核架构、统一开发语言 ArkTS 和多模态交互支持。开发者可借助 DevEco Studio 4.0 快速上手,体验高效、灵活的开发过程。 239个字符
1258 13
鸿蒙HarmonyOS应用开发 |鸿蒙技术分享HarmonyOS Next 深度解析:分布式能力与跨设备协作实战
|
存储 SpringCloudAlibaba Java
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论。
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
688 1

推荐镜像

更多
  • DNS