• Spring中异步注解@Async失效的几种原因
  • 发布于 1个月前
  • 83 热度
    0 评论
@Async是Spring提供的异步注解,在以下几种情况下,是会失效的,开发中需要注意。
在同一个类中使用
比如以下代码
@RestController
public class MyController {

    @GetMapping("/updateUser")
    public Boolean updateUser(){
        doUpdateUser();
        return true;
    }

    @Async
    public void doUpdateUser() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
@Async注解被应用在doUpdateUser方法上,但是这个方法却是在同一个类中被调用的,而不是通过代理对象来调用。Spring对@Async注解的支持是通过AOP实现的,它会创建一个代理对象来调用被注解的方法,以便异步执行。因此,如果你在同一个类中直接调用带有@Async注解的方法,异步功能会失效。要解决这个问题,你需要将doUpdateUser方法移动到另一个类中,并且通过在另一个类的实例上调用该方法来确保@Async注解的生效。另外,确保异步方法被Spring扫描到并且被正确地配置。修改后:
@RestController
public class MyController {

    @Resource
    MyService myService;

    @GetMapping("/updateUser")
    public Boolean updateUser(){
        myService.doUpdateUser();
        return true;
    }

}
Service

@Service
public class MyService {

    @Async
    public void doUpdateUser() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
启动类没有加@EnableAsync
如果你想使用Spring的异步功能,需要在启动类上添加 @EnableAsync注解以启用异步处理。这会告诉Spring在应用程序上下文中激活异步方法的支持。如果不添加这个注解,异步功能可能不会生效。
@SpringBootApplication
@EnableAsync
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }
}
返回值不是void或者future
因为是在独立线程,其他返回值不能识别。Spring可能会将其视为同步方法,从而导致异步失效

必须是public方法,也不能加static
@Async注解的异步方法通常要求是public的,因为Spring通过代理生成异步方法的实现,而代理需要能够访问方法。如果方法是private或protected,代理无法访问它,因此会导致异步失效。至于static关键字,Spring异步方法通常在运行时通过代理机制实现,而static方法是属于类而非实例的。由于代理是基于实例的,因此static方法无法被代理。因此,Spring要求异步方法不要使用static修饰符,以确保代理能够正确生成和调用异步方法。

扩展延伸
@Async与@Transactional一起使用会导致事务失效?
你可以看到网上有这么一段言论,在Async 方法上标注@Transactional是没用的,但在Async 方法调用的方法上标注@Transactional 是有效的。原因还言之凿凿,因为Spring的事务是绑定线程的,@Async会开启一个新的线程,导致事务失效。我们来做一个试验:
@Async
@Transactional
public void doUpdateUser() {
    userService.removeById(13);
    userService.removeById(14);
    throw new RuntimeException("我就是要抛出异常!");
}
结果是事务生效了,如果把方法拆开来,为了防止@Async失效,重新写一个Service:
@Service
public class MyService2 {
    // 堆代码 duidaima.com
    @Resource
    UserService userService;
    @Async
    public void doUpdate2() {
        userService.removeById(13);
        userService.removeById(14);
        throw new RuntimeException("我就是要抛出异常!");
    }

}
然后原来的service加了事务再去调:
@Service
public class MyService {

    @Resource
    MyService2 myService2;


    @Transactional
    public void doUpdateUser() {
        myService2.doUpdate2();
    }
}
发现事务就失效了,发现问题没,其实这个言论是有歧义的,准确来说是必须在事务方法中调用了@Async的异步方法,才会导致事务失效。如果@Async和@Transactional是不会失效的。那为什么@Async和@Transactional不会失效呢?这是因为@Async注解有关的拦截器实现了Ordered接口:图片并且返回的是最高级,所以会在事务执行之前启动异步线程,还是在一个线程里面,所以也就不会影响事务啦。

用户评论