SpringBoot異步調(diào)用相同類的解決方案
在之前的文章中,我們學(xué)習(xí)了Spring Boot中的異步調(diào)用機(jī)制,以及如何使用@Async注解來(lái)實(shí)現(xiàn)方法的異步執(zhí)行。然而,在實(shí)際開發(fā)中,我們可能會(huì)遇到這樣一個(gè)問(wèn)題:在同一個(gè)類中調(diào)用帶有@Async注解的方法時(shí),異步調(diào)用竟然失效了!這究竟是為什么呢?今天,我們就來(lái)深入探討一下這個(gè)問(wèn)題,并給出幾種可行的解決方案。
一、問(wèn)題介紹
在Spring Boot中,@Async注解被廣泛應(yīng)用于實(shí)現(xiàn)方法的異步執(zhí)行。通常,我們只需要在方法上添加@Async注解,并在啟動(dòng)類上添加@EnableAsync注解,就可以實(shí)現(xiàn)方法的異步調(diào)用了。然而,當(dāng)我們?cè)谕粋€(gè)類中調(diào)用帶有@Async注解的方法時(shí),卻發(fā)現(xiàn)異步調(diào)用并沒(méi)有生效,方法仍然是同步執(zhí)行的。
原因分析
這個(gè)問(wèn)題的根源在于Spring的AOP(面向切面編程)代理機(jī)制。在Spring中,@Async注解的實(shí)現(xiàn)依賴于AOP代理。當(dāng)我們?cè)谝粋€(gè)類中調(diào)用另一個(gè)方法時(shí),如果直接通過(guò)this來(lái)調(diào)用(即類內(nèi)部調(diào)用),那么實(shí)際上并沒(méi)有通過(guò)Spring的代理對(duì)象來(lái)調(diào)用該方法,因此@Async注解也就無(wú)法生效了。
二、解決方案
2.1 將異步方法拆分到另一個(gè)Bean中
這是最簡(jiǎn)單也是最推薦的一種解決方案。我們可以將帶有@Async注解的異步方法拆分到另一個(gè)Bean中,然后在原類中注入這個(gè)Bean,并通過(guò)調(diào)用這個(gè)Bean的方法來(lái)實(shí)現(xiàn)異步執(zhí)行。
步驟:
創(chuàng)建異步方法所在的Bean:
@Service public class AsyncService { @Async public void asyncMethod() { // 異步方法的實(shí)現(xiàn) System.out.println("Async method starts"); try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println("Async method ends"); } }
在原類中注入異步Bean并調(diào)用:
@Service public class MyService { @Autowired private AsyncService asyncService; public void myMethod() { System.out.println("My method starts"); asyncService.asyncMethod(); System.out.println("My method ends"); } }
實(shí)現(xiàn)效果:
- 當(dāng)調(diào)用
myMethod
時(shí),asyncMethod
會(huì)在獨(dú)立的線程中異步執(zhí)行。 - 控制臺(tái)輸出順序:
My method starts
->My method ends
->Async method starts
->Async method ends
(注意這里的順序可能會(huì)因?yàn)榫€程調(diào)度而有所差異)。
2.2 自注入當(dāng)前Bean
如果出于某種原因,我們不想將異步方法拆分到另一個(gè)Bean中,那么可以考慮使用自注入的方式。即在原類中注入自己,然后通過(guò)注入的實(shí)例來(lái)調(diào)用異步方法。
步驟: 在原類中注入自己:
@Service public class MyService { @Autowired private MyService self; public void myMethod() { System.out.println("My method starts"); self.asyncMethod(); System.out.println("My method ends"); } @Async public void asyncMethod() { // 異步方法的實(shí)現(xiàn) System.out.println("Async method starts"); try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println("Async method ends"); } }
注意:使用自注入需要確保沒(méi)有循環(huán)依賴的問(wèn)題。通常,如果只是方法調(diào)用的話,可能不會(huì)有問(wèn)題。但為了避免潛在的風(fēng)險(xiǎn),我們可以使用@Lazy注解來(lái)延遲Bean的加載。
實(shí)現(xiàn)效果:
- 當(dāng)調(diào)用
myMethod
時(shí),asyncMethod
會(huì)在獨(dú)立的線程中異步執(zhí)行。 - 控制臺(tái)輸出順序與上一種方案類似。
2.3 使用AopContext獲取代理對(duì)象
另一種解決方案是使用AopContext來(lái)獲取當(dāng)前類的代理對(duì)象,并通過(guò)代理對(duì)象來(lái)調(diào)用異步方法。但這種方法需要開啟exposeProxy選項(xiàng),并且代碼可能不夠直觀。
步驟:
在啟動(dòng)類上開啟exposeProxy:
@SpringBootApplication @EnableAsync @EnableAspectJAutoProxy(exposeProxy = true) public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
在方法中使用AopContext獲取代理對(duì)象:
@Service public class MyService { public void myMethod() { System.out.println("My method starts"); ((MyService) AopContext.currentProxy()).asyncMethod(); System.out.println("My method ends"); } @Async public void asyncMethod() { // 異步方法的實(shí)現(xiàn) System.out.println("Async method starts"); try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println("Async method ends"); } }
實(shí)現(xiàn)效果:
- 當(dāng)調(diào)用
myMethod
時(shí),asyncMethod
會(huì)在獨(dú)立的線程中異步執(zhí)行。 - 控制臺(tái)輸出順序與前面兩種方案類似。
注意:使用AopContext獲取代理對(duì)象的方法需要確保AOP代理已經(jīng)正確暴露,并且代碼中存在類型轉(zhuǎn)換,可能不夠安全。因此,在實(shí)際開發(fā)中需要謹(jǐn)慎使用。
2.4 使用ApplicationContext獲取Bean實(shí)例
還有一種方法是使用ApplicationContext來(lái)獲取Bean實(shí)例,并通過(guò)該實(shí)例來(lái)調(diào)用異步方法。但這種方法可能會(huì)有循環(huán)依賴的問(wèn)題,或者代碼看起來(lái)不夠直觀。
步驟: 在類中注入ApplicationContext:
@Service public class MyService { @Autowired private ApplicationContext applicationContext; public void myMethod() { System.out.println("My method starts"); MyService myService = applicationContext.getBean(MyService.class); myService.asyncMethod(); System.out.println("My method ends"); } @Async public void asyncMethod() { // 異步方法的實(shí)現(xiàn) System.out.println("Async method starts"); try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println("Async method ends"); } }
實(shí)現(xiàn)效果:
- 當(dāng)調(diào)用
myMethod
時(shí),asyncMethod
會(huì)在獨(dú)立的線程中異步執(zhí)行。 - 控制臺(tái)輸出順序與前面幾種方案類似。
注意:使用ApplicationContext獲取Bean實(shí)例的方法可能會(huì)有循環(huán)依賴的問(wèn)題,特別是在復(fù)雜的依賴關(guān)系中。因此,在實(shí)際開發(fā)中需要謹(jǐn)慎使用,并確保沒(méi)有引入新的問(wèn)題。
總結(jié)
- 推薦方案:將異步方法拆分到另一個(gè)Bean,代碼結(jié)構(gòu)清晰且符合Spring設(shè)計(jì)。
- 自注入:適用于簡(jiǎn)單場(chǎng)景,需注意循環(huán)依賴。
- AopContext:靈活但需額外配置,適合無(wú)法修改類結(jié)構(gòu)的情況。
以上就是SpringBoot異步調(diào)用相同類的方法的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot異步調(diào)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
到此這篇關(guān)于SpringBoot異步調(diào)用相同類的解決方案的文章就介紹到這了,更多相關(guān)SpringBoot異步調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java構(gòu)造函數(shù)示例(構(gòu)造方法)
這篇文章主要介紹了java構(gòu)造函數(shù)示例(構(gòu)造方法),需要的朋友可以參考下2014-03-03一文詳解SpringBoot中CommandLineRunner接口
Spring Boot的CommandLineRunner接口是一個(gè)函數(shù)式接口,用于在Spring Boot應(yīng)用程序啟動(dòng)后執(zhí)行一些初始化操作,它提供了一個(gè)run方法,該方法在應(yīng)用程序啟動(dòng)后被調(diào)用,本文給大家詳細(xì)介紹了SpringBoot中CommandLineRunner接口,需要的朋友可以參考下2023-10-10SpringBoot使用jasypt實(shí)現(xiàn)數(shù)據(jù)庫(kù)信息脫敏的方法詳解
這篇文章主要介紹了SpringBoot使用jasypt實(shí)現(xiàn)數(shù)據(jù)庫(kù)信息的脫敏,以此來(lái)保護(hù)數(shù)據(jù)庫(kù)的用戶名username和密碼password(容易上手,詳細(xì)),文中有詳細(xì)的圖文講解和代碼示例供大家參考,需要的朋友可以參考下2024-06-06spring boot 自定義參數(shù)過(guò)濾器,把傳入的空字符轉(zhuǎn)換成null方式
這篇文章主要介紹了spring boot 自定義參數(shù)過(guò)濾器,把傳入的空字符轉(zhuǎn)換成null方式。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08MyBatis 實(shí)現(xiàn)數(shù)據(jù)的批量新增和刪除的操作
這篇文章主要介紹了MyBatis 實(shí)現(xiàn)數(shù)據(jù)的批量新增和刪除的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Spring Boot2配置Swagger2生成API接口文檔詳情
這篇文章主要介紹了Spring Boot2配置Swagger2生成API接口文檔詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09