springboot異步@Async的使用及失效場(chǎng)景介紹
引言
在 java 中很多業(yè)務(wù)涉及到異步線程,比如在業(yè)務(wù)流處理時(shí),需要發(fā)短信發(fā)郵件通知用戶,或者需要上傳一些文件資源到其他服務(wù)器這種耗時(shí)的操作。
這種比較耗時(shí)的操作如果都在主線程里處理會(huì)阻塞整理流程,而且我們也不需要等待處理結(jié)果之后再進(jìn)行下一步操作,這時(shí)候就可以使用異步線程進(jìn)行處理,這樣主線程不會(huì)因?yàn)檫@些耗時(shí)的操作而阻塞,保證主線程的流程可以正常進(jìn)行。
一、@Async 使用位置
- 在方法上使用該@Async注解,表明該方法是一個(gè)異步任務(wù)
- 在類上面使用該@Async注解,表明該類中的所有方法都是異步任務(wù)
二、@Async 使用
在 Springboot 中使用 @Async:
- @Async 注解在使用時(shí),如果不指定線程池的名稱,則使用默認(rèn)的線程池,Spring默認(rèn)的線程池為
SimpleAsyncTaskExecutor
。 - 方法上一旦標(biāo)記了這個(gè) @Async 注解,當(dāng)其它線程調(diào)用這個(gè)方法時(shí),就會(huì)開(kāi)啟一個(gè)新的子線程去異步處理該業(yè)務(wù)邏輯。
- 啟動(dòng)類中增加
@EnableAsync
注解
代碼示例:
本文不做新配置,全部使用默認(rèn)的線程池及配置。
(1)創(chuàng)建 AsyncService 寫(xiě)異步方法
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncService { @Async public void Async() throws InterruptedException { Thread.sleep(10000); System.out.println("程序睡眠結(jié)束"); }
(2)編寫(xiě)業(yè)務(wù)層 AsyncTestService
package com.ruoyi.system.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncTestService { @Autowired private AsyncService asyncService; public String testSync() throws InterruptedException { System.out.println("開(kāi)始進(jìn)入方法"); System.out.println("進(jìn)行時(shí)"); System.out.println("正在進(jìn)行時(shí)"); asyncService.Async(); return "成功"; } }
(3)編寫(xiě)接口 AsyncTestController
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(value = "/async/lost") public class AsyncTestController { @Autowired private AsyncTestService asyncTestService; @RequestMapping(value = "/test", method = {RequestMethod.POST}) public String testAsync() throws InterruptedException { asyncTestService.testSync(); System.out.println("調(diào)用Controller控制器結(jié)束"); return "成功"; } }
postman 測(cè)試:
從下圖測(cè)試結(jié)果可知,打印語(yǔ)句 “調(diào)用Controller控制器結(jié)束” 在 “程序睡眠結(jié)束” 之前執(zhí)行,異步實(shí)現(xiàn)成功,沒(méi)有影響到主線程的任務(wù)。
三、注解 @Async 失效的情況
(1)調(diào)用同一個(gè)類中的異步方法(內(nèi)部調(diào)用)
從上面的 @Async 使用我們看到,添加注解的異步方法在一個(gè)單獨(dú)的類 AsyncService 中,然后注入到 AsyncTestService 中進(jìn)行調(diào)用。如果異步方法和調(diào)用的方法在同一個(gè)類中,還會(huì)正常進(jìn)行異步調(diào)用嗎?
AsyncTestService 代碼如下:
@Service public class AsyncTestService { public String testSync() throws InterruptedException { System.out.println("開(kāi)始進(jìn)入方法"); System.out.println("進(jìn)行時(shí)"); System.out.println("正在進(jìn)行時(shí)"); Async(); return "成功"; } @Async public void Async() throws InterruptedException { Thread.sleep(10000); System.out.println("程序睡眠結(jié)束"); } }
postman 測(cè)試:
如上圖所示,打印語(yǔ)句 “調(diào)用Controller控制器結(jié)束” 在 “程序睡眠結(jié)束” 之后執(zhí)行,說(shuō)明異步并沒(méi)有生效,程序還是走了主線程。
這是因?yàn)?nbsp;@Async注解是通過(guò)aop代理實(shí)現(xiàn)的,需要通過(guò)JDK動(dòng)態(tài)代理或者cglib,生成代理對(duì)象。異步的功能,是在代理對(duì)象中增加的,我們必須調(diào)用代理對(duì)象的 testSync() 方法才行。而在類中直接進(jìn)行方法的內(nèi)部調(diào)用,在 testSync() 方法中調(diào)用Async()方法,調(diào)用的是該類原對(duì)象的Async方法,相當(dāng)于調(diào)用了this.Async()方法,而并非AsyncTestService 代理類的 Async() 方法。 因此,像這種內(nèi)部方法調(diào)用,@Async注解的異步功能會(huì)失效。
要想在同一個(gè)類中調(diào)用異步方法,需要使用 ApplicationContext
獲得到該類。
AsyncTestService 代碼如下:
import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncTestService { @Autowired private ApplicationContext applicationContext; public String testSync() throws InterruptedException { AsyncTestService asyncBean = applicationContext.getBean(AsyncTestService.class); System.out.println("當(dāng)前對(duì)象是否是代理對(duì)象:" + AopUtils.isAopProxy(asyncBean)); System.out.println("是否是cglib代理對(duì)象:" + AopUtils.isCglibProxy(asyncBean)); System.out.println("是否是jdk代理對(duì)象:" + AopUtils.isJdkDynamicProxy(asyncBean)); System.out.println(asyncBean == this); asyncBean.Async(); return "成功"; } @Async public void Async() throws InterruptedException { Thread.sleep(10000); System.out.println("程序睡眠結(jié)束"); } }
postman 測(cè)試:
打印控制器語(yǔ)句在前,異步實(shí)現(xiàn)成功。
(2)未使用 @EnableAsync 注解
在 Springboot 中要開(kāi)啟@Async注解異步的功能,需要在項(xiàng)目的啟動(dòng)類,或者配置類上,使用@EnableAsync注解。
@EnableAsync注解相當(dāng)于一個(gè)開(kāi)關(guān),控制是否開(kāi)啟@Async注解異步的功能,默認(rèn)是關(guān)閉的。
@EnableAsync @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
(3)注解@Async的方法不是public方法
異步的方法必須是 public修飾的,否則 aop 代理異常,異步失效。
(4)返回值錯(cuò)誤
注解 @Async 的返回值只能為 void
或 Future
。
(5)方法用static修飾了
使用@Async注解聲明的方法,必須是能被重寫(xiě)的,很顯然static修飾的方法,是類的靜態(tài)方法,是不允許被重寫(xiě)的。
因此這種情況下,@Async注解的異步功能會(huì)失效。
(6)類中需要使用@Autowired或@Resource等注解自動(dòng)注入,不能自己手動(dòng)new對(duì)象
(7)方法用final修飾
(8)業(yè)務(wù)類沒(méi)加@Service注解
到此這篇關(guān)于springboot異步@Async的使用及失效場(chǎng)景介紹的文章就介紹到這了,更多相關(guān)springboot異步@Async內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解讀Java和JavaScript區(qū)別與聯(lián)系
這篇文章主要介紹了解讀Java和JavaScript區(qū)別與聯(lián)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02Spring如何基于xml實(shí)現(xiàn)聲明式事務(wù)控制
這篇文章主要介紹了Spring如何基于xml實(shí)現(xiàn)聲明式事務(wù)控制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10淺談MyBatis循環(huán)Map(高級(jí)用法)
這篇文章主要介紹了淺談MyBatis循環(huán)Map(高級(jí)用法),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09VScode 打造完美java開(kāi)發(fā)環(huán)境最新教程
這篇文章主要介紹了VScode 打造完美java開(kāi)發(fā)環(huán)境最新教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Java使用String.format方法格式化字符串的示例詳解
在編程過(guò)程中,我們經(jīng)常需要?jiǎng)?chuàng)建格式化的字符串來(lái)滿足特定的需求,比如生成用戶友好的消息、構(gòu)建報(bào)告或是輸出調(diào)試信息,Java 提供了一個(gè)強(qiáng)大的工具——String.format 方法,本文給大家介紹了Java使用String.format方法格式化字符串的示例,需要的朋友可以參考下2024-11-11java去除空格、標(biāo)點(diǎn)符號(hào)的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于java去除空格、標(biāo)點(diǎn)符號(hào)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):循環(huán)鏈表和棧
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之循環(huán)鏈表、棧的實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Java數(shù)據(jù)結(jié)構(gòu)中循環(huán)鏈表、棧、的功能、定義及使用方法,需要的朋友可以參考下2021-08-08