Java中@Async異步失效的9種場(chǎng)景
前言
最近有粉絲問(wèn)了我一個(gè)問(wèn)題:他在項(xiàng)目某個(gè)方法使用@Async
注解,但是該方法還是同步
執(zhí)行了,異步
不起作用,到底是什么原因呢?
偽代碼如下:
@Slf4j @Service public?class?UserService?{ ????@Async ????public?void?async(String?value)?{ ????????log.info("async:" + value); ????} }
這個(gè)問(wèn)題還是比較有意思的,今天這篇文章總結(jié)了@Async注解失效的9種場(chǎng)景,希望對(duì)你會(huì)有所幫助。
1 未使用@EnableAsync注解
在Spring中要開(kāi)啟@Async注解異步的功能,需要在項(xiàng)目的啟動(dòng)類,或者配置類上,使用@EnableAsync
注解。
例如:
@EnableAsync @SpringBootApplication(exclude?=?{DataSourceAutoConfiguration.class}) public?class?Application?{ ????public?static?void?main(String[]?args)?{ ????????SpringApplication.run(Application.class,?args); ????} }
@EnableAsync
注解相當(dāng)于一個(gè)開(kāi)關(guān)
,控制是否開(kāi)啟@Async
注解異步的功能,默認(rèn)是關(guān)閉的。
如果在項(xiàng)目的啟動(dòng)類上沒(méi)使用@EnableAsync注解,則@Async注解異步的功能不生效。
2 內(nèi)部方法調(diào)用
我們?cè)谌粘i_(kāi)發(fā)中,經(jīng)常需要在一個(gè)方法中調(diào)用另外一個(gè)方法,例如:
@Slf4j @Service public?class?UserService?{ ????public?void?test()?{ ????????async("test"); ????} ????@Async ????public?void?async(String?value)?{ ????????log.info("async:{}",?value); ????} }
這個(gè)示例中,在UserService類中的test()方法中調(diào)用了async()方法。
如果在controller中@Autowired了UserService類的對(duì)象,調(diào)用了它的test()方法,則async()異步的功能會(huì)失效。
我們知道Spring通過(guò)@Async注解實(shí)現(xiàn)異步的功能,底層其實(shí)是通過(guò)Spring的AOP
實(shí)現(xiàn)的,也就是說(shuō)它需要通過(guò)JDK動(dòng)態(tài)代理
或者cglib
,生成代理對(duì)象
。
異步的功能,是在代理對(duì)象中增加的,我們必須調(diào)用代理對(duì)象的test()方法才行。
而在類中直接進(jìn)行方法的內(nèi)部調(diào)用,在test()方法中調(diào)用async()方法,調(diào)用的是該類原對(duì)象的async方法,相當(dāng)于調(diào)用了this.async()方法,而并非UserService代理類的async()方法。
因此,像這種內(nèi)部方法調(diào)用,@Async注解的異步功能會(huì)失效。
3 方法非public
在Java中有4種權(quán)限修飾符
public
:所有類都可以訪問(wèn)。private
:只能同一個(gè)類訪問(wèn)。protected
:同一個(gè)類,同一個(gè)包下的其他類,不同包下的子類可以訪問(wèn)。默認(rèn)修飾符
:同一個(gè)類,同一個(gè)包下的其他類可以訪問(wèn)。
在實(shí)際工作中,我們使用頻率最高的可能是public和private了。
如果我在定義Service類中的某個(gè)方法時(shí),有時(shí)把權(quán)限修飾符定義錯(cuò)了,例如:
@Slf4j @Service public?class?UserService?{ ????@Async ????private?void?async(String?value)?{ ????????log.info("async:{}",?value); ????} }
這個(gè)例子中將UserService類的async()方法的權(quán)限修飾符定義成了private的,這樣@Async注解也會(huì)失效。
因?yàn)閜rivate修飾的方法,只能在UserService類的對(duì)象中使用。
而@Async注解的異步功能,需要使用Spring的AOP生成UserService類的代理對(duì)象,該代理對(duì)象沒(méi)法訪問(wèn)UserService類的private方法,因此會(huì)出現(xiàn)@Async注解失效的問(wèn)題。
4 方法返回值錯(cuò)誤
我們?cè)趯?xiě)一個(gè)新的方法時(shí),經(jīng)常需要定義方法的返回值。
返回值可以是void、int、String、User等等,但如果返回值定義錯(cuò)誤,也可能會(huì)導(dǎo)致@Async注解的異步功能失效。
例如:
@Service public?class?UserService?{ ????@Async ????public?String?async(String?value)?{ ????????log.info("async:{}",?value); ????????return?value; ????} }
UserService類的async方法的返回值是String,這種情況竟然會(huì)導(dǎo)致@Async注解的異步功能失效。
在AsyncExecutionInterceptor類的invoke()方法,會(huì)調(diào)用它的父類AsyncExecutionAspectSupport中的doSubmit方法,該方法時(shí)異步功能的核心代碼,如下:
從圖中看出,@Async注解的異步方法的返回值,要么是Future,要么是null。
因此,在實(shí)際項(xiàng)目中,如果想要使用@Async注解的異步功能,相關(guān)方法的返回值必須是void
或者Future
。
5 方法用static修飾了
有時(shí)候,我們的方法會(huì)使用static修飾,這樣在調(diào)用的地方,可以直接使用類名.方法名,訪問(wèn)該方法了。
但如果在@Async方法上加了static修飾符,例如:
@Slf4j @Service public?class?UserService?{ ????@Async ????public?static?void?async(String?value)?{ ????????log.info("async:{}",?value); ????} }
這時(shí)@Async的異步功能會(huì)失效,因?yàn)檫@種情況idea會(huì)直接報(bào)錯(cuò):Methods annotated with '@Async' must be overridable 。
使用@Async注解聲明的方法,必須是能被重寫(xiě)的,很顯然static修飾的方法,是類的靜態(tài)方法,是不允許被重寫(xiě)的。
因此這種情況下,@Async注解的異步功能會(huì)失效。
6 方法用final修飾
在Java種final關(guān)鍵字,是一個(gè)非常特別的存在。
用final修飾的類,沒(méi)法被繼承。
用final修飾的方法,沒(méi)法被重寫(xiě)。
用final修飾的變量,沒(méi)法被修改。
如果final使用不當(dāng),也會(huì)導(dǎo)致@Async注解的異步功能失效,例如:
@Slf4j @Service public?class?UserService?{ ????public?void?test()?{ ????????async("test"); ????} ????@Async ????public??final?void?async(String?value)?{ ????????log.info("async:{}",?value); ????} }
這種情況下idea也會(huì)直接報(bào)錯(cuò):Methods annotated with '@Async' must be overridable 。
因?yàn)槭褂胒inal關(guān)鍵字修飾的方法,是沒(méi)法被子類重寫(xiě)的。
因此這種情況下,@Async注解的異步功能會(huì)失效。
7 業(yè)務(wù)類沒(méi)加@Service注解
有時(shí)候,我們?cè)谛录覵ervice類時(shí),會(huì)忘了加@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注解異步的功能也不會(huì)生效。因?yàn)閁serService類沒(méi)有使用@Service、@Component或者@Controller等注解聲明,該類不會(huì)被Spring管理,因此也就無(wú)法使用Spring的異步功能。
8 自己new的對(duì)象
在項(xiàng)目中,我們經(jīng)常需要new一個(gè)對(duì)象,然后對(duì)他賦值,或者調(diào)用它的方法。
但如果new了一個(gè)Service類的對(duì)象,可能會(huì)出現(xiàn)一些意想不到的問(wèn)題,例如:
@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了一個(gè)UserService類的對(duì)象,然后調(diào)用該對(duì)象的async()方法。
很顯然這種情況下,async()方法只能同步執(zhí)行,沒(méi)法異步執(zhí)行。
因?yàn)樵陧?xiàng)目中,我們自己new的對(duì)象,不會(huì)被Spring管理,因此也就無(wú)法使用Spring的異步功能。
不過(guò)我們可以通過(guò)BeanPostProcessor
類,將創(chuàng)建的對(duì)象手動(dòng)注入到Spring容器中。
9 Spring無(wú)法掃描異步類
我們?cè)赟pring項(xiàng)目中可以使用@ComponentScan
注解指定項(xiàng)目中掃描的包路徑,例如:
@ComponentScan({"com.susan.demo.service1"}) @SpringBootApplication(exclude?=?{DataSourceAutoConfiguration.class}) public?class?Application?{ ????public?static?void?main(String[]?args)?{ ????????SpringApplication.run(Application.class,?args); ????} }
項(xiàng)目中com.susan.demo.service1這個(gè)路徑是不存在的,會(huì)導(dǎo)致@Async注解異步的功能失效。
同時(shí)如果@ComponentScan注解定義的路徑,沒(méi)有包含你新加的Servcie類的路徑,@Async注解異步的功能也會(huì)失效。
以上就是Java中@Async異步失效的9種場(chǎng)景的詳細(xì)內(nèi)容,更多關(guān)于Java @Async異步失效的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot中redis的緩存穿透問(wèn)題實(shí)現(xiàn)
這篇文章主要介紹了springboot中redis的緩存穿透問(wèn)題實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02Spring Cloud使用Feign實(shí)現(xiàn)Form表單提交的示例
本篇文章主要介紹了Spring Cloud使用Feign實(shí)現(xiàn)Form表單提交的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Mybatis基于注解形式的sql語(yǔ)句生成實(shí)例代碼
這篇文章主要介紹了 Mybatis基于注解形式的sql語(yǔ)句生成實(shí)例代碼,需要的朋友可以參考下2017-09-09Spring Cloud動(dòng)態(tài)配置刷新@RefreshScope與@Component的深度解析
在現(xiàn)代微服務(wù)架構(gòu)中,動(dòng)態(tài)配置管理是一個(gè)關(guān)鍵需求,Spring Cloud 提供了 @RefreshScope 注解,允許應(yīng)用在運(yùn)行時(shí)動(dòng)態(tài)更新配置,而無(wú)需重啟服務(wù),本文深入探析Spring Cloud動(dòng)態(tài)配置刷新@RefreshScope與@Component,感興趣的朋友一起看看吧2025-04-04Springboot+WebSocket實(shí)現(xiàn)一對(duì)一聊天和公告的示例代碼
這篇文章主要介紹了Springboot+WebSocket實(shí)現(xiàn)一對(duì)一聊天和公告的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04AJAX?SpringBoot?前后端數(shù)據(jù)交互的項(xiàng)目實(shí)現(xiàn)
本文主要介紹了AJAX?SpringBoot?前后端數(shù)據(jù)交互的項(xiàng)目實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Java中數(shù)組的一些常見(jiàn)操作和技巧分析
這篇文章主要給大家介紹了關(guān)于Java中數(shù)組的一些常見(jiàn)操作和技巧分析的相關(guān)資料,數(shù)組(Array)是Java中的一種引用數(shù)據(jù)類型,是多個(gè)相同類型數(shù)據(jù)一定順序排列的集合,并使用一個(gè)名字命名,并通過(guò)編號(hào)的方式對(duì)這些數(shù)據(jù)進(jìn)行統(tǒng)一管理,需要的朋友可以參考下2023-08-08