Spring AOP实现声明式事务机制源码解析

目录
  • 一、声明式全局事务
  • 二、源码
  • 三、小结:

一、声明式全局事务

Seata示例工程中,能看到@GlobalTransactional,如下方法示例:

@GlobalTransactional
public boolean purchase(long accountId, long stockId, long quantity) {
    String xid = RootContext.getXID();
    LOGGER.info("New Transaction Begins: " + xid);
    boolean stockResult = reduceAccount(accountId,stockId, quantity);
    if (!stockResult) {
        throw new RuntimeException("账号服务调用失败,事务回滚!");
    }
    Long orderId = createOrder(accountId, stockId, quantity);
    if (orderId == null || orderId <= 0) {
        throw new RuntimeException("订单服务调用失败,事务回滚!");
    }
    return true;
}

purchase方法上加上此注解,即表示此方法内的reduceAccountcreateOrder两个微服务调用也将加入到分布式事务中,即扣除账户余额与创建订单将具有分布式事务的数据一致性保障能力。

了解 Spring 注解事务实现的话,应该也能推测出,Seata 的事务能力也可能是基于 Spring 的 AOP 机制,给标注了@GlobalTransactional 的方法做 AOP 增加,织入额外的逻辑以完成分布式事务的能力,伪代码大致如下:

GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
try {
    tx.begin(xxx);
    ...
    purchase(xxx)//给purchase增加全局事务处理能力
    ...
    tx.commit();
} catch (Exception exx) {
    tx.rollback();
    throw exx;
}

本篇就介绍Seata 如何使用 Spring AOP 来将注解变成分布式事务的代码。

二、源码

在上一篇《Seata1.6源码全局事务注解@GlobalTransactional的识别》bean,并对这类 bean 添加GlobalTransactionalInterceptor,进行 AOP 增强,加入分布式事务的能力。本篇延续这个话题继续,梳理 AOP 增强的逻辑。

通过下边的调用堆栈帮大家梳理出 在源码AbstractAutoProxyCreator#wrapIfNecessary中有 createProxy的调用。

createProxy:443, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
wrapIfNecessary:344, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
wrapIfNecessary:307, GlobalTransactionScanner (io.seata.spring.annotation)
postProcessAfterInitialization:293, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
applyBeanPostProcessorsAfterInitialization:455, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
initializeBean:1808, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:620, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:542, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)

AbstractAutoProxyCreator#createProxy其中new ProxyFactory()则是 AOP 的关键。

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        // 为目标 Bean 的 BeanDefinition 对象设置一个属性
        // org.springframework.aop.framework.autoproxy.AutoProxyUtils.originalTargetClass -> 目标 Bean 的 Class 对象
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }
    // <1> 创建一个代理工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    // <2> 复制当前 ProxyConfig 的一些属性(例如 proxyTargetClass、exposeProxy)
    proxyFactory.copyFrom(this);
    /**
     * <3> 判断是否类代理,也就是是否开启 CGLIB 代理
     * 默认配置下为 `false`,参考 {@link org.springframework.context.annotation.EnableAspectJAutoProxy}
     */
    if (!proxyFactory.isProxyTargetClass()) {
        /*
         * <3.1> 如果这个 Bean 配置了进行类代理,则设置为 `proxyTargetClass` 为 `true`
         */
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            /*
             * <3.2> 检测当前 Bean 实现的接口是否包含可代理的接口
             * 如没有实现,则将 `proxyTargetClass` 设为 `true`,表示需要进行 CGLIB 提升
             */
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
    /*
     * <4> 对入参的 Advisor 进一步处理,因为其中可能还存在 Advice 类型,需要将他们包装成 DefaultPointcutAdvisor 对象
     * 如果配置了 `interceptorNames` 拦截器,也会添加进来
     */
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // <5> 代理工厂添加 Advisor 数组
    proxyFactory.addAdvisors(advisors);
    // <6> 代理工厂设置 TargetSource 对象
    proxyFactory.setTargetSource(targetSource);
    // <7> 对 ProxyFactory 进行加工处理,抽象方法,目前没有子类实现
    customizeProxyFactory(proxyFactory);
    proxyFactory.setFrozen(this.freezeProxy);
    // <8> 是否这个 AdvisedSupport 配置管理器已经过滤过目标类(默认为 false)
    if (advisorsPreFiltered()) {
        // 设置 `preFiltered` 为 `true`
        // 这样 Advisor 们就不会根据 ClassFilter 进行过滤了,而直接通过 MethodMatcher 判断是否处理被拦截方法
        proxyFactory.setPreFiltered(true);
    }
    // Use original ClassLoader if bean class not locally loaded in overriding class loader
    ClassLoader classLoader = getProxyClassLoader();
    if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
       classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
    }
    // <9> 通过 ProxyFactory 代理工厂创建代理对象
    return proxyFactory.getProxy(getProxyClassLoader());
}

上边源码读起来很生硬,对于我们使用来梳理核心源码流程来说,留意 AOP 实现的几个关键要素即可:

  • 配置target,被代理者(类或方法中有标注@GlobalTransactional的bean),最终还是要调用他么的方法
  • 配置接口,即代理要具备的功能
  • 配置额外的切面 addAdvisors,这里是指定GlobalTransactionalInterceptor
  • 根据ClassLoader 类加载器创建代理

由此我们可以推测中分布式事务的逻辑是在 GlobalTransactionalInterceptor 中,核心逻辑的实现应该就是invoke中,我们从GlobalTransactionalInterceptor#invoke源码中理一理:

@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
    Class<?> targetClass =
        methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
    Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
    if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
        final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
        // 获取方法上的@GlobalTransactional注解中的内容
        final GlobalTransactional globalTransactionalAnnotation =
            getAnnotation(method, targetClass, GlobalTransactional.class);
        // 获取方法上的@GlobalLock注解中的内容
        final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
        //判断是否禁用或者降级状态
        boolean localDisable = disable || (ATOMIC_DEGRADE_CHECK.get() && degradeNum >= degradeCheckAllowTimes);
        if (!localDisable) {
            if (globalTransactionalAnnotation != null || this.aspectTransactional != null) {
                AspectTransactional transactional;
                if (globalTransactionalAnnotation != null) {
                    //构建事务描述信息,这些基础配置信息很重要
                    transactional = new AspectTransactional(globalTransactionalAnnotation.timeoutMills(),
                        globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(),
                        globalTransactionalAnnotation.rollbackForClassName(),
                        globalTransactionalAnnotation.noRollbackFor(),
                        globalTransactionalAnnotation.noRollbackForClassName(),
                        globalTransactionalAnnotation.propagation(),
                        globalTransactionalAnnotation.lockRetryInterval(),
                        globalTransactionalAnnotation.lockRetryTimes(),
                        globalTransactionalAnnotation.lockStrategyMode());
                } else {
                    transactional = this.aspectTransactional;
                }
                //若是@GlobalTransactional
                return handleGlobalTransaction(methodInvocation, transactional);
            } else if (globalLockAnnotation != null) {
                //若是@GlobalLock
                return handleGlobalLock(methodInvocation, globalLockAnnotation);
            }
        }
    }
    return methodInvocation.proceed();
}

handleGlobalTransaction中开始了重点,transactionalTemplate从其名字可知,这是模板方法模式,new TransactionalExecutor()getTransactionInfo是在构建事务的一些基础信息,execute()中则是指定了事务目标方法(如purchase方法),

Object handleGlobalTransaction(final MethodInvocation methodInvocation,
    final AspectTransactional aspectTransactional) throws Throwable {
    boolean succeed = true;
    try {
        return transactionalTemplate.execute(new TransactionalExecutor() {
            @Override
            public Object execute() throws Throwable {
                return methodInvocation.proceed();
            }
            ...
            @Override
            public TransactionInfo getTransactionInfo() {
                // reset the value of timeout
                int timeout = aspectTransactional.getTimeoutMills();
                if (timeout <= 0 || timeout == DEFAULT_GLOBAL_TRANSACTION_TIMEOUT) {
                    timeout = defaultGlobalTransactionTimeout;
                }
                TransactionInfo transactionInfo = new TransactionInfo();
                transactionInfo.setTimeOut(timeout);
                transactionInfo.setName(name());
                ...
                return transactionInfo;
            }
        });
    } catch (TransactionalExecutor.ExecutionException e) {
        ...
        }
    } finally {
        if (ATOMIC_DEGRADE_CHECK.get()) {
            EVENT_BUS.post(new DegradeCheckEvent(succeed));
        }
    }
}

execute方法中的内容是重点

public Object execute(TransactionalExecutor business) throws Throwable {
    // 1. Get transactionInfo
    TransactionInfo txInfo = business.getTransactionInfo();
    if (txInfo == null) {
        throw new ShouldNeverHappenException("transactionInfo does not exist");
    }
    // 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
    GlobalTransaction tx = GlobalTransactionContext.getCurrent();
    // 1.2 Handle the transaction propagation.
    Propagation propagation = txInfo.getPropagation();
    SuspendedResourcesHolder suspendedResourcesHolder = null;
    try {
        //...
        // 1.3 If null, create new transaction with role 'GlobalTransactionRole.Launcher'.
        //若全局事务上下文未就绪则new DefaultGlobalTransaction();
        if (tx == null) {
            tx = GlobalTransactionContext.createNew();
        }
        // set current tx config to holder
        GlobalLockConfig previousConfig = replaceGlobalLockConfig(txInfo);
        try {
            // 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
            //    else do nothing. Of course, the hooks will still be triggered.
            //2. 开启全局事务,
            // 2.1 triggerBeforeBegin()
                // 2.2 会跟TC通信获取全局事务ID:xid,
                // 2.3 RootContext.bind(xid);
            // 2.4 triggerAfterBegin()的事件通知调用
            beginTransaction(txInfo, tx);
            Object rs;
            try {
                // 执行我们的事务方法如`purchase`方法
                rs = business.execute();
            } catch (Throwable ex) {
                // 3. 遇到 business exception 则回滚
                completeTransactionAfterThrowing(txInfo, tx, ex);
                throw ex;
            }
            // 4. 提交事务,触发事件回调
            // 4.1 triggerBeforeCommit();
                // 4.2 tx.commit();与TC通信提交事务,内部默认是有5次重试机会
            // 4.3 triggerAfterCommit();
            commitTransaction(tx, txInfo);
            //返回结果
            return rs;
        } finally {
            //5. clear
            resumeGlobalLockConfig(previousConfig);
            //结束后的回调
            triggerAfterCompletion();
            cleanUp();
        }
    } finally {
        // If the transaction is suspended, resume it.
        if (suspendedResourcesHolder != null) {
            tx.resume(suspendedResourcesHolder);
        }
    }
}

三、小结:

本篇梳理了引入seata-spring-boot-starter模块后,其内部会通过的自动装配机制会在SeataAutoConfiguration类中,扫描具有@GlobalTransactional全局事务注解的类和方法的 bean,并通过ProxyFactory机制对这类 bean 进行AOP代理, 添加GlobalTransactionalInterceptor,在其内部invoke中通过transactionalTemplate加入分布式事务的能力:

  • 开启事务与 TC 进行通信,获取 xid ,注入事务上下文
  • 调用目标方法
  • 之后根据结果是否正常执行二阶段的提交或回滚

但这里仅仅是 TM 的能力,仍未到RM的职能边界。

以上就是Spring AOP实现声明式事务机制源码解析的详细内容,更多关于Spring AOP声明式事务的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot使用AOP与注解实现请求参数自动填充流程详解

    首先定义一个加在方法上的注解 import java.lang.annotation.*; /** * 开启自动参数填充 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented @Inherited public @interface AutoParameterFill { /** * 要填充的字段名,不写的话默认下面类的子类中的字段都要填充 * * @see AutoParameterFi

  • Spring AOP的概念与实现过程详解

    目录 Aop 实现aop方式一 实现aop方式二 注解实现aop Aop 什么是Aop? AOP就是面向切面编程,通过预编译方式以及运行期间的动态代理技术来实现程序的统一维护功能. 什么是切面,我理解的切面就是两个方法之间,两个对象之间,两个模块之间就是一个切面.假设在两个模块之间需要共同执行一系列操作,并且最后将这一系列操作注入到两个模块之间的指定位置.此时这一系列操作就是切面,注入这些操作的位置称之为切点. 举例:公司员工上班 A员工上班需要在前台进行打卡,同样的B员工…其他员工都需要在前台

  • Spring使用AOP完成统一结果封装实例demo

    目录 Spring使用AOP完成统一结果封装 Demo实现 Spring使用AOP完成统一结果封装 起因:自己写项目的时候忍受不了每个方法都要写一个retrun Result.success();和 retrun Result.error();,同时想到项目运行时异常的统一捕捉处理,于是我就在想有没有一种方法能够快捷有效的实现统一返回结果格式的方法.同时也能够比较方便的设置各种参数方便使用,于是我就想到AOP. Demo实现 引入依赖 <dependency> <groupId>o

  • Spring AOP统一功能处理示例代码

    目录 1. 什么是Spring AOP? 2. 为什要用 AOP? 3. Spring AOP 应该怎么学习呢? 3.1AOP组成 3.1.1 切面(Aspect) 3.1.2 连接点(Join Point) 3.1.3 切点(Pointcut) 3.1.4 通知(Advice) 3.2 Spring AOP实现 3.2.1 添加 AOP 框架支持 3.2.2 定义切面和切点. 3.2.3 定义相关通知 3.3 Spring AOP 实现原理 3.3.1 动态代理 3.3.2 JDK和CGLIB

  • Spring AOP源码深入分析

    目录 1. 前言 2. 术语 3. 示例 4. @EnableAspectJAutoProxy 5. AbstractAutoProxyCreator 6. 构建Advisor 7. 创建代理对象 8. DynamicAdvisedInterceptor 9. CglibMethodInvocation 10. Advice子类 1. 前言 Spring除了IOC和DI,还有另一个杀手锏功能——Spring AOP.AOP是一种面向切面的编程思想,它的关注点是横向的,不同于OOP的纵向.面向对象

  • Spring AOP与AspectJ的对比及应用详解

    目录 1 简介 2 Spring AOP vs AspectJ 2.1 织入方式 2.2 Joinpoints 2.3 性能 3 Spring Boot使用AspectJ 3.1 引入依赖 3.2 被AOP的对象 3.3 配置Aspect 3.4 maven插件 3.5 执行及测试 3.6 一些遇到的错误 4 总结 1 简介 AOP,即面向切面编程是很常用的技术,特别是在Java Web开发中.而最流行的AOP框架分别是Spring AOP和AspectJ. 2 Spring AOP vs As

  • Spring AOP如何自定义注解实现审计或日志记录(完整代码)

    目录 环境准备 项目结构 自定义审计注解 定义切面类 定义返回值处理基类 定义返回值处理子类 定义功能模块类 定义操作类 定义审计信息实体类 书写mapper文件 开启AOP拦截 注解配置 总结 环境准备 JDK 1.8,Springboot 2.1.3.RELEASE,spring-boot-starter-aop.2.1.4.RELEASE.jar,aspectjrt.1.9.2.jar,aspectjweaver.1.9.2.jar,pom依赖如下: <!-- 添加aspectj -->

  • Spring AOP实现声明式事务机制源码解析

    目录 一.声明式全局事务 二.源码 三.小结: 一.声明式全局事务 在Seata示例工程中,能看到@GlobalTransactional,如下方法示例: @GlobalTransactional public boolean purchase(long accountId, long stockId, long quantity) { String xid = RootContext.getXID(); LOGGER.info("New Transaction Begins: " +

  • Spring AOP实现Redis缓存数据库查询源码

    应用场景 我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 需要解决的问题 操作缓存的代码写在哪?必须要做到与业务逻辑代码完全分离. 如何避免脏读? 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识?即通过该标识(Redis中为Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个key.只有这样才能保证缓存内容的正确性 如何序列化查询结果?查询结果可能是单个实体

  • 详解Redis 缓存删除机制(源码解析)

    删除的范围 过期的 key 在内存满了的情况下,如果继续执行 set 等命令,且所有 key 都没有过期,那么会按照缓存淘汰策略选中的 key 过期删除 redis 中设置了过期时间的 key 会单独存储一份 typedef struct redisDb { dict *dict; // 所有的键值对 dict *expires; //设置了过期时间的键值对 // ... } redisDb; 设置有效期 Redis 中有 4 个命令可以给 key 设置过期时间,分别是 expire pexpi

  • React事件机制源码解析

    React v17里事件机制有了比较大的改动,想来和v16差别还是比较大的. 本文浅析的React版本为17.0.1,使用ReactDOM.render创建应用,不含优先级相关. 原理简述 React中事件分为委托事件(DelegatedEvent)和不需要委托事件(NonDelegatedEvent),委托事件在fiberRoot创建的时候,就会在root节点的DOM元素上绑定几乎所有事件的处理函数,而不需要委托事件只会将处理函数绑定在DOM元素本身. 同时,React将事件分为3种类型--d

  • Kubernetes controller manager运行机制源码解析

    目录 Run StartControllers ReplicaSet ReplicaSetController syncReplicaSet Summary Run 确立目标 理解 kube-controller-manager 的运行机制 从主函数找到run函数,代码较长,这里精简了一下 func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error { // configz 模块,在kube-scheduler分析中已经了解

  • Spring声明式事务注解之@EnableTransactionManagement解析

    Spring声明式事务注解之@EnableTransactionManagement 1. 说明 @EnableTransactionManagement声明在主配置类上,表示开启声明式事务,其原理是通过@Import导入TransactionManagementConfigurationSelector组件,然后又通过TransactionManagementConfigurationSelector导入组件AutoProxyRegistrar和ProxyTransactionManageme

  • 详解Spring学习之声明式事务管理

    前言 在前面的小节中,我们学习了关于事务的概念以及事务管理的重要性,并且通过编程使用Spring的编程式事务管理进行操作,加深对事务管理的重要性的学习,不过,由于编程式的事务管理使用起来不是很方便,所以在日常的开发中基本不怎么使用,接下来的内容我们将学习使用Spring的声明式事务管理,这里有一个地方需要明白的是,Spring的声明式事务管理的实现方式其实是通过AOP的方式来实现的,也就是为原始的事务管理对象创建代理对象,从而实现事务管理增强的 基于TransactionProxyFactory

  • Spring Security 基于URL的权限判断源码解析

    目录 1. FilterSecurityInterceptor 源码阅读 2. 自定义基于url的授权 1. FilterSecurityInterceptor 源码阅读 org.springframework.security.web.access.intercept.FilterSecurityInterceptor 通过过滤器实现对HTTP资源进行安全处理. 该安全拦截器所需的 SecurityMetadataSource 类型为 FilterInvocationSecurityMetad

  • Spring注解 TX声明式事务实现过程解析

    环境搭建导入 maven依赖 <!--spring提供的数据库操作工具--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!--c3p0 数据库连接池--> &

  • jquery事件绑定解绑机制源码解析

    引子 为什么Jquery能实现不传回调函数也能解绑事件?如下: $("p").on("click",function(){ alert("The paragraph was clicked."); }); $("#box1").off("click"); 事件绑定解绑机制 调用on函数的时候,将生成一份事件数据,结构如下: { type: type, origType: origType, data: da

随机推荐