美文网首页Spring Boot
自定义AOP与事务

自定义AOP与事务

作者: lyz999999 | 来源:发表于2021-07-10 21:52 被阅读0次

    背景

           最近在写一个业务实现:主线程生成订单,然后使用自定义 AOP 的 @AfterReturning 注解,通过新建线程异步查询该条订单并推送到其他系统。
           开发环境中,整个流程执行一直没问题(重复测试了很多遍)。一放到线上,问题就出来了。异步查询这一步,在线上一直查不到新生成的这条订单。但主线程返回后会调起支付,在支付业务中能正常查到。(.............莫名其妙的bug)

    分析

    当前环境(线上/线下)

    1. 数据库为默认事务隔离级别REPEATABLE-READ可重复读。
    2. Spring 中开启了事务。

    问题产生原因

    1. 主线程与异步子线程不属于同一个事务。
      Spring的事务是通过ThreadLocal来保证线程安全的,使用ThreadLocal来存储Connection,不同的线程Connection不一样,所以不在同一个事务中。
    2. 在主线程事务未提交时,异步子线程无法读取到主线程事务中的数据。

    为什么线下开发环境中没有出现问题?

           自我分析:极有可能是线下开发环境中,机器执行速度较慢,每次执行到异步子线程中查询前,主线程都已经提交了事务;而线上机器执行速度快,执行异步子线程查询操作时,主线程还未提交事务,导致查询不存来。

    解决问题

    主要思路

    1. 控制事务范围,即使主线程中事务在自定义 AOP 执行之前,提交事务。
    2. Spring 中事务也是使用 AOP 来控制的,通过设置order值来控制,切面加载顺序。
    3. order值越大,优先级越小,反之越大。

    order取值范围

    package org.springframework.core;
    
    public interface Ordered {
        int HIGHEST_PRECEDENCE = -2147483648; // 2^31
        int LOWEST_PRECEDENCE = 2147483647; // 2^31 - 1
    
        int getOrder();
    }
    
    注:其实所有切面order值不能取到最高优先级,会报错
    java.lang.IllegalStateException: No MethodInvocation found: Check that an AOP invocation is in progress, 
    and that the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, 
    note that advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor!
    

           ExposeInvocationInterceptor是一个调用器的拦截器,打开起源吗可以看到

    @Override
    public int getOrder() {
            return PriorityOrdered.HIGHEST_PRECEDENCE + 1; //即-2147483648+1
    }
    

           所有AOP切面(包括事务)优先级在这个值之前都会报错,所以order值设置应大于该值

    自定义 AOP 切面类中注解设置加载顺序

    @Component
    @Aspect
    @Order(-200)
    public class McGoodsAop {
    }
    
    注:初步测试,@Order注解写在方法中,不生效

    Springboot中设置事务加载优先级

    @SpringBootApplication
    @EnableTransactionManagement(order = 0) // order = 0 设置事务加载优先级
    public class ApiApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ApiApplication.class, args);
        }
    
    }
    

    总结

           1. order值设置的是加载顺序,如事务优先级大于自定义 AOP 切面,则 AOP 切面内业务将被包含在事务中,可控制事务回滚等;如事务优先级小于自定义 AOP 切面,则在执行自定义AOP中代码时,事务已经完成(提交)。
           2. 事务和自定义AOP默认优先级(不主动设置)均为最低级,优先级相同情况下,以事务 AOP 优先。
           3. 因为用的是返回通知@AfterRunning,刚开始以为事务优先级高会在执行自定义 AOP 中代码前提交事务(这是错误的),导致耽误了不少时间。
           4. 事务和自定义 AOP 修改order值,热部署都不生效,需要重启应用。

    order优先级补充

           通过aop编写的代码会在运行时进行织入(动态代理),织入的过程类似于方法调用,是一个入栈出栈的过程。


    入栈出栈示意图

    相关文章

      网友评论

        本文标题:自定义AOP与事务

        本文链接:https://www.haomeiwen.com/subject/nkrlpltx.html