zoukankan      html  css  js  c++  java
  • Spring @Async/@Transactional 失效的原因及解决方案

    这周开发自测刚好遇到了使用@Transactional和@Async的不生效的问题,参考网上资料后,发现这篇文章图文并茂,讲的非常清晰易懂,简单做了些补充搬运至此。

    实现AOP的方法有动态代理、编译期,类加载期织入等等,Spring实现AOP的方法则就是利用了动态代理机制,正因如此,才会导致某些情况下@Async和@Transactional不生效。

    @EnableAsync //添加此注解开启异步调用
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }

    当某些任务执行时间较长,且客户端不需要及时获取结果(如调用第三方API),只要在需要异步调用的任务上添加 @Async即可,如:

    @Async //可以@Async("")指定自定义线程池(beanName)
    void asyncTask(String keyword) {
        try {
            // 模拟处理过程  
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            //logger
            //error tracking
        }
        System.out.println(keyword);
    }

    这样asyncTask就会异步执行。 然而,如果在同一个Class内(比如业务逻辑同在一个XXXServiceImpl),出现下面这样的情况,先调用一个非异步任务

    private void noAsyncTask(String keyword){
        asyncTask(keyword); //该方法内再调用异步方法
    }
    
    @Async
    void asyncTask(String keyword) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            //logger
            //error tracking
        }
        System.out.println(keyword);
    }

    此时,@Async是没有生效的。 原因就是@Async和@Transaction利用了动态代理机制。

    当Spring发现@Transactional或者@Async时,会自动生成一个ProxyObject,如:

    此时调用Class.transactionTask会调用ProxyClass.产生事务操作。
    然而当Class里的一个非事务方法调用了事务方法,ProxyClass是这样的:

    到这里应该可以看明白了,如果调用了noTransactionTask方法,最终会调用到Class.transactionTask,而这个方法是不带有任何Transactional的信息的,也就是@Transactional根本没有生效哦。
    简单来说就是: 同一个类内这样调用的话,只有第一次调用了动态代理生成的ProxyClass,之后一直用的是不带任何切面信息的方法本身。

    知道了原因,处理方法也特别简单,就是让noTransactionTask里依旧调用ProxyClass的transactionTask方法:只需要显示利用Spring暴露的AopContext即可。代码如下:

    private void noAsyncTask(String keyword){
        // 注意这里 调用了代理类的方法, 或者直接在spring上下文获取bean(如果有aop,单例池中存放的是织入aop的代理类)
        ((YourClass) AopContext.currentProxy()).asyncTask(keyword);
    //
    SpringContextUtil.getBean(YourClass.class).asyncTask(keyword);
    } @Async void asyncTask(String keyword) { try { Thread.sleep(5000); } catch (InterruptedException e) { //logger //error tracking  } System.out.println(keyword); }
    
    
    如果使用AopContext记得要在Class上加上@EnableAspectJAutoProxy(exposeProxy = true)来暴露AOP的Proxy对象才行,否则会报错。

    或者就可以把这样的方法放到另外一个类里,不要产生类里一个非异步/非事务方法,调用了异步/事务方法,不过大家协同开发同一个文件的话,谁能保证没有人这样调用呢?总而言之无论什么方案,都是使得调用ProxyObject的方法。

    如有错误,请批评指正。

     
     
    =========割了更健康===============
    参考原文
    作者:陶源0111
    链接:https://www.jianshu.com/p/9a0de6577ed7
  • 相关阅读:
    使用 ASP.NET Core MVC 创建 Web API(五)
    使用 ASP.NET Core MVC 创建 Web API(四)
    使用 ASP.NET Core MVC 创建 Web API(三)
    使用 ASP.NET Core MVC 创建 Web API(二)
    使用 ASP.NET Core MVC 创建 Web API(一)
    学习ASP.NET Core Razor 编程系列十九——分页
    学习ASP.NET Core Razor 编程系列十八——并发解决方案
    一个屌丝程序猿的人生(九十八)
    一个屌丝程序猿的人生(九十七)
    一个屌丝程序猿的人生(九十五)
  • 原文地址:https://www.cnblogs.com/steve-jiang/p/12692299.html
Copyright © 2011-2022 走看看