關(guān)于Spring注解@Async引發(fā)其他注解失效的解決
概述
在前面一篇文章中,介紹,在一個(gè)Bean中注入自己,如果有@Async和@Transaction,如果使用@Autowire注入自身,會(huì)報(bào)循環(huán)依賴(lài),如果使用BeanFactoryAware注入自己,會(huì)使得@Transaction失效。 例如:
@Service
public class MyService implements BeanFactoryAware{
private MyService self;
//事務(wù)注解無(wú)效
@Transactional
public void notWork() {
...
}
@Async
public Future async(){
...
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
self= beanFactory.getBean(MyService.class);
}
}
當(dāng)時(shí)只是簡(jiǎn)單提了一下,這篇文章就是來(lái)介紹為什么會(huì)失效。
一般情況
造成上面的情況需要滿(mǎn)足以下條件:
- 有@Async和其他類(lèi)似@Transaction注解
- 自己類(lèi)在BeanFactoryAware中,通過(guò)BeanFactory獲取自己
造成的結(jié)果:除@Async外的注解生效,其他的都不生效,如下圖

而正常代理的應(yīng)該是下圖:

原因
首先想到的是@Async注解的處理方式可能和其他的不一樣。在AsyncAnnotationBeanPostProcessor的實(shí)現(xiàn)中(具體代碼是在其父類(lèi)AbstractAdvisingBeanPostProcessor),發(fā)現(xiàn)一個(gè)問(wèn)題,
正常情況下,進(jìn)來(lái)的bean已經(jīng)是被代理的動(dòng)態(tài)代理類(lèi),而失效的時(shí)候,進(jìn)來(lái)的確實(shí)實(shí)際的類(lèi),如下圖:

然后在分析下代碼,如果是實(shí)際的類(lèi),走到69行的時(shí)候,返回是true,把@Aysnc的Advisor加入到動(dòng)態(tài)道理中,而如果是實(shí)際的類(lèi),走到83行的時(shí)候,就會(huì)創(chuàng)建代理類(lèi),只把@Aysnc的advisor加入到動(dòng)態(tài)代理中,所已諸如@Transaction就會(huì)失效。
為什么進(jìn)來(lái)的不是代理類(lèi)
其實(shí)唯一的區(qū)別就是BeanFactoryAware中,是否通過(guò)了BeanFactory獲取了自己。那為什么使用BeanFactory獲取了自己,后續(xù)的BeanPostProcessor中就不是代理了?如果熟悉Spring @Transaction加載機(jī)制的就知道,諸如@Transaction,@Retryable 注解的動(dòng)態(tài)代理創(chuàng)建是在AnnotationAwareAspectJAutoProxyCreator中創(chuàng)建的。通過(guò)debug發(fā)現(xiàn),經(jīng)過(guò)AnnotationAwareAspectJAutoProxyCreator后,我們的動(dòng)態(tài)代理竟然沒(méi)有加上。
再看一下AnnotationAwareAspectJAutoProxyCreator中的實(shí)現(xiàn),但是經(jīng)過(guò)他卻沒(méi)有生成代理類(lèi)。原因竟然是提前暴露的Map里面竟然有“myService”,

他是什么時(shí)候暴露出來(lái)的呢?其實(shí)就是在
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
self= beanFactory.getBean(MyService.class);
}
那么,一切水落石出了,在實(shí)例MyService中,觸發(fā)了BeanFactoryAware,通過(guò)beanFactory.getBean(MyService.class);中創(chuàng)建了代理類(lèi)(tips:當(dāng)前代理類(lèi)并沒(méi)有包含@Async的Adivisor),因?yàn)楝F(xiàn)在Spring其實(shí)正是在創(chuàng)建MyService這個(gè)Bean,還沒(méi)有放入到BeanFactory中。然后我們?cè)龠@個(gè)過(guò)程中又觸發(fā)了一次beanFactory.getBean(MyService.class);導(dǎo)致創(chuàng)建代理并返回后,加入到了到了提前暴露的map中。導(dǎo)致后面的一系列問(wèn)題。感覺(jué)有點(diǎn)繞??磮D說(shuō)話(huà):
正常情況,應(yīng)該是如下流程:

異常情況卻是這樣的

小結(jié)
正常情況下,還是使用@Autowire來(lái)注入把(如果使用Autowire,上述情況直接回拋出循環(huán)依賴(lài))。當(dāng)然,出現(xiàn)了問(wèn)題,也是不能放過(guò)了,要知其然還要知其所以然!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java -jar設(shè)置添加啟動(dòng)參數(shù)實(shí)現(xiàn)方法
這篇文章主要介紹了java -jar設(shè)置添加啟動(dòng)參數(shù)實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
SpringBoot使用@Validated處理校驗(yàn)的方法步驟
@Validated?注解的主要目的是啟用和利用?Spring?的驗(yàn)證框架,它可以用于類(lèi)上也可以用于方法參數(shù)上,本文給大家介紹了SpringBoot使用@Validated優(yōu)雅的處理校驗(yàn)的方法步驟,通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08
SpringBoot自定義線(xiàn)程池,執(zhí)行定時(shí)任務(wù)方式
這篇文章主要介紹了SpringBoot自定義線(xiàn)程池,執(zhí)行定時(shí)任務(wù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
Java?Selenium實(shí)現(xiàn)修改打開(kāi)頁(yè)面窗口大小
Selenium是一個(gè)強(qiáng)大的自動(dòng)化測(cè)試工具,支持多種編程語(yǔ)言和瀏覽器,本文將詳細(xì)介紹如何使用Java?Selenium來(lái)修改打開(kāi)頁(yè)面窗口的大小,需要的可以參考下2025-01-01
Java中List.contains(Object?object)方法使用
本文主要介紹了Java中List.contains(Object?object)方法,使用List.contains(Object?object)方法判斷ArrayList是否包含一個(gè)元素對(duì)象,感興趣的可以了解一下2022-04-04
springboot+maven快速構(gòu)建項(xiàng)目的示例代碼
本篇文章主要介紹了springboot+maven快速構(gòu)建項(xiàng)目的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
springMVC發(fā)送郵件的簡(jiǎn)單實(shí)現(xiàn)
本篇文章主要介紹了springMVC發(fā)送郵件的簡(jiǎn)單實(shí)現(xiàn) ,主要是利用利用javax.mail發(fā)送郵件,圖片與附件都可發(fā)送,有興趣的可以了解一下2017-04-04
Mac下安裝配置Maven并在IDEA中配置的詳細(xì)教程
這篇文章主要介紹了Mac下安裝配置Maven并在IDEA中配置,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Spring Data Jpa Mysql使用utf8mb4編碼的示例代碼
這篇文章主要介紹了Spring Data Jpa Mysql使用utf8mb4編碼的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11

