Spring 事務、異步和循環(huán)依賴有什么關系?

前言
在循環(huán)依賴中有一種循環(huán)依賴,就是自注入:自己依賴自己。

事務的自注入
在 Spring 自調用事務失效,你是怎么解決的? 有小伙伴提出可以自己注入自己來解決事務失效。
具體使用方式如下:
@Slf4j
@Service
public?class?OrderBizServiceImpl?implements?OrderBizService?{
????//?注入自己
????@Autowired
????private?OrderBizService?orderBizService;
????@Override
????public?void?callBack()?throws?Exception?{
????????//?一系列的邏輯
????????//?需要事務操作更新訂單和用戶金額
????????orderBizService.updateOrderStatusAndUserBalance();
????}
????@Override
????@Transactional(rollbackFor?=?Exception.class)
????public?void?updateOrderStatusAndUserBalance()?throws?Exception?{
????????//?內部是事務邏輯
????}
}
是不是發(fā)現(xiàn)很神奇的事情,事務生效了。
其實這里注入自己,其實是注入的一個代理對象,調事務,也是調的代理對象的事務,所以事務生效。
Spring 事務失效原因:
事務只能應用到 public 方法上才會有效;事務需要從外部調用,Spring 自調用會失效;建議事務注解 @Transactional 一般添加在實現(xiàn)類上。
異步的自注入
發(fā)現(xiàn) @Transactional 注解可以自注入解決事務失效的問題,在某次開發(fā)中,自然而然想到 @Async 異步是不是也可以自注入解決循環(huán)依賴的問題。
NO, NO, NO……
事實告訴我們是不可以的!

從錯誤開始著手:

開始往上面反推 exposedObject == bean 是這一塊出了問題。
也就是說異步的時候,再次從二級緩存中獲取的和初始的不相同。
Object earlySingletonReference = getSingleton(beanName, false);

這一次獲取的時候發(fā)現(xiàn)不同所以報錯。
那就開始 Debug, 按照循環(huán)依賴的邏輯,執(zhí)行到 populateBean 時,屬性賦值,發(fā)現(xiàn)有依賴自己,此時會創(chuàng)建自己。
執(zhí)行 singleton.getObject 方法


而此時執(zhí)行 getEarlyBeanReference 先判斷 InfrastructureAdvisorAutoProxyCreator true 調用 wrapIfNecessary 判斷是否生成一個代理對象,這里并沒有生成代理對象。

然后開始執(zhí)行異步的 AsyncAnnotationBeanPostProcessor 判斷為 false。所以沒有執(zhí)行異步的生成代理對象邏輯。
那就繼續(xù)往下看

進入到 initializeBean 的邏輯,有一部分叫做 applyBeanPostProcessorsAfterInitialization
方面小伙伴搜索,所以貼出來代碼關鍵字。IDEA 使用
? + Shift + F搜索。

循環(huán)執(zhí)行后置處理器:


發(fā)現(xiàn)執(zhí)行完 AsyncAnnotationBeanPostProcessor 這個 PostProcessor 后,對象被改變了。從而導致二級緩存和當前的 Bean 不同。
以上也就是為什么 @Async 自調用不可以,因為在后面初始化階段被代理修改了對象。
@Transactional 為什么可以呢?


先判斷 InfrastructureAdvisorAutoProxyCreator true 生成一個代理對象。

事務的處理器 PersistenceExceptionTranslationPostProcessor 也沒有執(zhí)行。
繼續(xù) Debug 關注 applyBeanPostProcessorsAfterInitialization

執(zhí)行結束,發(fā)現(xiàn) Bean 沒有發(fā)生改變。
總結
@Transactional: 是在循環(huán)依賴從二級緩存升到三級緩存的時候已經(jīng)生成了代理對象。 @Async: 是在初始化階段(initializeBean)去生成代理對象。然后 @Async 導致后面判斷 exposedObject == bean為 false ,從而拋出異常。

可以看出圖中有兩處會執(zhí)行 BeanPostProcessor :
在 singletonFactory.getObject 時,如果是 SmartInstantiationAwareBeanPostProcessor 的子類會執(zhí)行 getEarlyBeanReference 方法。 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 時會執(zhí)行所有 BeanPostProcessor 的 postProcessAfterInitialization 的方法。
也有其他的地方在執(zhí)行后置處理器,比如 applyBeanPostProcessorsBeforeInitialization ,只不過這里關注這倆處。
而這兩處都有可能生成代理對象, @Transactional 是在 getEarlyBeanReference 處生成的代理對象,所以后面判斷 Bean 是否被改變時為 true,而 @Async 是在后面異步生成了代理對象,所以判斷不通過。
至此,分析完畢,錯誤之處,歡迎指正
-
歷史文章 |?相關推薦

