@Async异步失效的9种场景

async · 浏览次数 : 19

小编点评

**@Async注解失效的原因** `@Async`注解失效的原因主要有以下几个方面: 1. **方法权限问题:** 在方法的权限修饰符中定义了`private`,这会阻止`@Async`注解在内部方法中使用该方法。 2. **方法返回值错误:** 方法返回值必须是 `void` 或 `Future`,如果返回值类型错误,也会导致异步功能失效。 3. **方法非static:** 使用 `static` 修饰的类无法使用 `@Async`注解,因为 `@Async`注解要求方法是可被重用的。 4. **方法没有被注入的bean:** 在使用 `@Async`注解时,目标方法的实例必须已在 Spring 中注册,才能使用。 5. **项目扫描路径问题:** 如果项目中没有包含新加的 Service 类路径,`@Async`注解将无法识别并执行该方法。 6. **方法权限问题:** 在 `@Async` 注解方法参数时,参数的权限必须与方法权限匹配(如果方法是 `private`,参数必须也是 `private`)。 7. **循环依赖:** 在使用 `@Async` 注解方法时,如果方法中包含循环依赖,也会导致异步功能失效。 8. **Bean创建问题:** 在使用 `@Async` 注解方法时,如果目标方法在 Spring 中没有被实例化,也会导致异步功能失效。 **解决方案** 1. 确保方法权限符合 `@Async` 的要求。 2. 检查方法返回值类型是否符合 `@Async` 的要求。 3. 将方法标记为 `static`。 4. 确保目标方法已在 Spring 中注册。 5. 确保项目扫描路径包含新加的 Service 类路径。 6. 确保方法参数的权限匹配方法权限。 7. 避免使用循环依赖。 8. 确保目标方法在 Spring 中实例化。 9. 确保所有方法在 `@Async` 注解的属性上声明正确。

正文

前言

最近星球中有位小伙伴问了我一个问题:他在项目某个方法使用@Async注解,但是还是该方法还是同步执行了,异步不起作用,到底是什么原因呢?

伪代码如下:

@Slf4j
@Service
public class UserService {

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

这个问题还是比较有意思的,今天这篇文章总结了@Async注解失效的9种场景,希望对你会有所帮助。

1 未使用@EnableAsync注解

在Spring中要开启@Async注解异步的功能,需要在项目的启动类,或者配置类上,使用@EnableAsync注解。

例如:

@EnableAsync
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@EnableAsync注解相当于一个开关,控制是否开启@Async注解异步的功能,默认是关闭的。

如果在项目的启动类上未使用@EnableAsync注解,则@Async注解异步的功能不生效。

2 内部方法调用

我们在日常开发中,经常需要在一个方法中调用另外一个方法,例如:

@Slf4j
@Service
public class UserService {

    public void test() {
        async("test");
    }

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

这个示例中,在UserService类中的test()方法中调用了async()方法。

如果在controller中@Autowired了UserService类的对象,调用了它的test()方法,则async()异步的功能会失效。

我们知道Spring通过@Async注解实现异步的功能,底层其实是通过Spring的AOP实现的,也就是说它需要通过JDK动态代理或者cglib,生成代理对象

异步的功能,是在代理对象中增加的,我们必须调用代理对象的test()方法才行。

而在类中直接进行方法的内部调用,在test()方法中调用async()方法,调用的是该类原对象的async方法,相当于调用了this.async()方法,而并非UserService代理类的async()方法。

因此,像这种内部方法调用,@Async注解的异步功能会失效。

3 方法非public

在Java中有4种权限修饰符

  • public:所有类都可以访问。
  • private:只能同一个类访问。
  • protected:同一个类,同一个包下的其他类,不同包下的子类可以访问。
  • 默认修饰符:同一个类,同一个包下的其他类可以访问。

在实际工作中,我们使用频率最高的可能是public和private了。

如果我在定义Service类中的某个方法时,有时把权限修饰符定义错了,例如:

@Slf4j
@Service
public class UserService {

    @Async
    private void async(String value) {
        log.info("async:{}", value);
    }
}

这个例子中将UserService类的async()方法的权限修饰符定义成了private的,这样@Async注解也会失效。

因为private修饰的方法,只能在UserService类的对象中使用。

而@Async注解的异步功能,需要使用Spring的AOP生成UserService类的代理对象,该代理对象没法访问UserService类的private方法,因此会出现@Async注解失效的问题。

4 方法返回值错误

我们在写一个新的方法时,经常需要定义方法的返回值。

返回值可以是void、int、String、User等等,但如果返回值定义错误,也可能会导致@Async注解的异步功能失效。

例如:

@Service
public class UserService {

    @Async
    public String async(String value) {
        log.info("async:{}", value);
        return value;
    }
}

UserService类的async方法的返回值是String,这种情况竟然会导致@Async注解的异步功能失效。

在AsyncExecutionInterceptor类的invoke()方法,会调用它的父类AsyncExecutionAspectSupport中的doSubmit方法,该方法时异步功能的核心代码,如下:


从图中看出,@Async注解的异步方法的返回值,要么是Future,要么是null。

因此,在实际项目中,如果想要使用@Async注解的异步功能,相关方法的返回值必须是void或者Future

5 方法用static修饰了

有时候,我们的方法会使用static修饰,这样在调用的地方,可以直接使用类名.方法名,访问该方法了。

但如果在@Async方法上加了static修饰符,例如:

@Slf4j
@Service
public class UserService {

    @Async
    public static void async(String value) {
        log.info("async:{}", value);
    }
}

这时@Async的异步功能会失效,因为这种情况idea会直接报错:Methods annotated with '@Async' must be overridable 。

使用@Async注解声明的方法,必须是能被重写的,很显然static修饰的方法,是类的静态方法,是不允许被重写的。

因此这种情况下,@Async注解的异步功能会失效。

6 方法用final修饰

在Java种final关键字,是一个非常特别的存在。

用final修饰的类,没法被继承。

用final修饰的方法,没法被重写。

用final修饰的变量,没法被修改。

如果final使用不当,也会导致@Async注解的异步功能失效,例如:

@Slf4j
@Service
public class UserService {

    public void test() {
        async("test");
    }

    @Async
    public  final void async(String value) {
        log.info("async:{}", value);
    }
}

这种情况下idea也会直接报错:Methods annotated with '@Async' must be overridable 。

因为使用final关键字修饰的方法,是没法被子类重写的。

因此这种情况下,@Async注解的异步功能会失效。

7 业务类没加@Service注解

有时候,我们在新加Service类时,会忘了加@Service注解,例如:

@Slf4j
//@Service
public class UserService {

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

@Service
public class TestService {

   @Autowired
   private UserService userService;

    public void test() {
        userService.async("test");
    }
}

这种情况下,@Async注解异步的功能也不会生效。因为UserService类没有使用@Service、@Component或者@Controller等注解声明,该类不会被Spring管理,因此也就无法使用Spring的异步功能。

最近就业形式比较困难,为了感谢各位小伙伴对苏三一直以来的支持,我特地创建了一些工作内推群, 看看能不能帮助到大家。
你可以在群里发布招聘信息,也可以内推工作,也可以在群里投递简历找工作,也可以在群里交流面试或者工作的话题。

进群方式,添加苏三的私人微信:su_san_java,备注:博客园+所在城市,即可加入。

8 自己new的对象

在项目中,我们经常需要new一个对象,然后对他赋值,或者调用它的方法。

但如果new了一个Service类的对象,可能会出现一些意想不到的问题,例如:

@Slf4j
@Service
public class UserService {

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

@Service
public class TestService {

    public void test() {
        UserService userService = new UserService();
        userService.async("test");
    }
}

在TestService类的test()方法中,new了一个UserService类的对象,然后调用该对象的async()方法。

很显然这种情况下,async()方法只能同步执行,没法异步执行。

因为在项目中,我们自己new的对象,不会被Spring管理,因此也就无法使用Spring的异步功能。

不过我们可以通过BeanPostProcessor类,将创建的对象手动注入到Spring容器中。

9 Spring无法扫描异步类

我们在Spring项目中可以使用@ComponentScan注解指定项目中扫描的包路径,例如:

@ComponentScan({"com.susan.demo.service1"})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

项目中com.susan.demo.service1这个路径是不存在的,会导致@Async注解异步的功能失效。

同时如果@ComponentScan注解定义的路径,没有包含你新加的Servcie类的路径,@Async注解异步的功能也会失效。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注苏三的公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。

与@Async异步失效的9种场景相似的内容:

@Async异步失效的9种场景

前言 最近星球中有位小伙伴问了我一个问题:他在项目某个方法使用@Async注解,但是还是该方法还是同步执行了,异步不起作用,到底是什么原因呢? 伪代码如下: @Slf4j @Service public class UserService { @Async public void async(Str

乾坤大挪移,如何将同步阻塞(sync)三方库包转换为异步非阻塞(async)模式?Python3.10实现。

众所周知,异步并发编程可以帮助程序更好地处理阻塞操作,比如网络 IO 操作或文件 IO 操作,避免因等待这些操作完成而导致程序卡住的情况。云存储文件传输场景正好包含网络 IO 操作和文件 IO 操作,比如业内相对著名的七牛云存储,官方sdk的默认阻塞传输模式虽然差强人意,但未免有些循规蹈矩,不够锐意

Vue 中 Promise 的then方法异步使用及async/await 异步使用总结

转载请注明出处: 1.Promise 的 then 方法使用 then 方法是 Promise 中 处理的是异步调用,异步调用是非阻塞式的,在调用的时候并不知道它什么时候结束,也就不会等到他返回一个有效数据之后再进行下一步处理; 想了解 Promise 的使用,可以看这篇文章: ES6 中 Prom

【C#异步】异步多线程的本质,上下文流转和同步

引言 net同僚对于async和await的话题真的是经久不衰,这段时间又看到了关于这方面的讨论,最终也没有得出什么结论,其实要弄懂这个东西,并没有那么复杂,简单的从本质上来讲,就是一句话,async 和await异步的本质就是状态机+线程环境上下文的流转,由状态机向前推进执行,上下文进行环境切换,

第126篇: 异步函数(async和await)

好家伙,本篇为《JS高级程序设计》第十章“期约与异步函数”学习笔记 ES8 的 async/await 旨在解决利用异步结构组织代码的问题。 为为此增加了两个新关键字:async 和 await。 1.async关键字 1.1.使用说明 async 关键字用于声明异步函数。 函数声明、函数表达式、箭

第127篇:异步函数(async和await)练习题(异步,消息队列)

好家伙,本篇为做题思考 书接上文 题目如下: 1.请给出下列代码的输出结果,并配合"消息队列"写出相关解释 async function foo() { console.log(2); console.log(await Promise.resolve(8)); console.log(9); }

如何在forEach内使用异步调用 async/await

翻自: How to use async and await in a forEach JS loop? https://learn.coderslang.com/0144-how-to-use-async-and-await-in-a-foreach-js-loop/ 事实上我们无法在 forEa

async/await初学者指南

> JavaScript中的`async`和`await`关键字提供了一种现代语法,帮助我们处理异步操作。在本教程中,我们将深入研究如何使用`async/await`来掌控JavaScript程序中的流程控制。 > ## 总览 - 如何创建JavaScript异步函数 - async关键字 - aw

Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现

普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法。 协程底

[转帖]InnoDB引擎之-两次写(Double Write)

https://www.jianshu.com/p/63f2985fb427 InnoDB引擎有几个重点特性,为其带来了更好的性能和可靠性: 插入缓冲(Insert Buffer) 两次写(Double Write) 自适应哈希索引(Adaptive Hash Index) 异步IO(Async I