1. 程式人生 > >SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

原創 專注JavaWeb開發 2018-12-24 17:30:33

Spring中@Async

在Java應用中,絕大多數情況下都是通過同步的方式來實現互動處理的;但是在處理與第三方系統互動的時候,容易造成響應遲緩的情況,之前大部分都是使用多執行緒來完成此類任務,其實,在spring 3.x之後,就已經內建了@Async來完美解決這個問題

有時候在使用的過程中@Async註解會失效(原因和@Transactional註解有時候會失效的原因一樣)。

下面定義一個Service:

兩個非同步執行的方法test03()和test02()用來模擬專案中可能出現的耗時操作,然後test()方法呼叫這兩個耗時的方法:

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

定義Controller:

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

執行方法,返回結果:

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

方法執行結果明顯與我們的預期不符,方法的輸出順序表示了test02()和test03()兩個非同步方法居然同步執行了,也就是說@Aysnc註解失效了!

失效的原因是因為我們是在test()方法中直接呼叫的test02()和test03()方法,相當於是this.test02()和this.test03()呼叫的,也就是說真正呼叫test02()和test03()方法的是TestService物件本身呼叫的,而@Async和@Transactional註解本質使用的是動態代理,

真正應該是TestService的代理物件呼叫test02()和test03()方法。其實Spring容器在初始化的時候Spring容器會將含有AOP註解的類物件“替換”為代理物件(簡單這麼理解),那麼註解失效的原因就很明顯了,就是因為呼叫方法的是物件本身而不是代理物件,因為沒有經過Spring容器,那麼解決方法也會沿著這個思路來解決。

網上有不少部落格說解決方法就是將要非同步執行的方法單獨抽取成一個類,這樣的確可以解決非同步註解失效的問題,原理就是當你把執行非同步的方法單獨抽取成一個類的時候,這個類肯定是被Spring管理的,其他Spring元件需要呼叫的時候肯定會注入進去,這時候實際上注入進去的就是代理類了,其實還有其他的解決方法,並不一定非要單獨抽取成一個類。

解決方式一:在TestService中通過上下文獲取自己的代理物件呼叫非同步方法

其實我們的注入物件都是從Spring容器中給當前Spring元件進行成員變數的賦值,由於TestService使用了AOP註解,那麼實際上TestService在Spring容器中實際存在的是它的代理物件。

SpringUtil工具類可以參考:http://mp.toutiao.com/preview_article/?pgc_id=6638488982025929223

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

執行結果,非同步方法非同步執行了:

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

解決方式二:開啟cglib代理,手動獲取Spring代理類

在啟動類上加上:

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

使用AopContext.currentProxy()獲取當前代理類:

這裡為了證明Spring容器中的物件就是當前代理類物件特地輸出了一句話:

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

執行結果:

SpringBoot使用Async註解失效分析、解決(spring非同步回撥)

 

OK,問題完美解決!

application.properties配置如下:

#java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
# 增加@EnableAspectJAutoProxy
spring.aop.auto=true
#開啟CGLIB代理
spring.aop.proxy-target-class=true