业务需求,用户下单后创建订单入库,并且发邮件及短信通知给发货人。由于前端在请求创建订单后需要拿到结果(成功/失败),并不关心短信及邮件的通知。所以通知的业务可以异步处理,不阻塞订单的创建,简单来说如果创建订单需要1秒,发送通知需要10秒,那么前端期望1秒之后拿到请求结果。而不是11秒。
在使用以下方法时,你需要确保Spring Boot应用的@EnableAsync注解已经开启,以启用Spring的异步支持。
解决方案1:使用@Async注解
先说一个错误用例,导致原因是@Async没有起到作用
//注意这是一个错误的用法,@Async没有起到作用
@Service
public class AsyncService {
@Async
public void executeAsyncTask() {
// 执行异步逻辑 B
}
public void executeMainLogic() {
// 执行主逻辑 A
// 调用异步方法,并立即返回
executeAsyncTask();
}
}
原因分析
如果在一个类中,方法B被@Async修饰,而方法A没有被@Async修饰,并且方法A调用了方法B,那么会导致@Async修饰的方法B的注解失效。原因是,对于对于加了@Async的方法B是通过SpringAOP机制生成的代理类执行的,方法A是直接调用这个类的方法,因此通过A调用B,会使得B也被Spring当成普通方法直接调用,从而使得注解失效。
详细可参考
正确的方式应该这样写
(当然也可以将异步方法B的调用放在另外一个Bean上,并通过依赖注入的方式使用该Bean。)
因为我这里B方法的入参,有A方法返回的结果,所以是在一个方法中调用,具体可以根据自己的情况来定
@Service
public class AsyncService {
@Async
public void executeAsyncTask() {
// 执行异步逻辑 B
}
public void executeMainLogic() {
// 执行主逻辑 A
// 调用异步方法,并立即返回,此处 SpringUtil 使用了hutool的包
// import cn.hutool.extra.spring.SpringUtil;
final AsyncService bean = SpringUtil.getApplicationContext().getBean(AsyncService.class);
bean.executeAsyncTask();
}
}
解决方案2:使用CompletableFuture
@Service
public class AsyncService {
public void executeMainLogic() {
// 执行主逻辑 A
// 调用异步方法,并立即返回
CompletableFuture.runAsync(this::executeAsyncTask);
}
public void executeAsyncTask() {
// 执行异步逻辑 B
}
}