Spring 事務(wù)、異步和循環(huán)依賴有什么關(guān)系?
前言
在循環(huán)依賴中有一種循環(huán)依賴,就是自注入:自己依賴自己。
事務(wù)的自注入
在 Spring 自調(diào)用事務(wù)失效,你是怎么解決的? 有小伙伴提出可以自己注入自己來(lái)解決事務(wù)失效。
具體使用方式如下:
- @Slf4j
- @Service
- public class OrderBizServiceImpl implements OrderBizService {
- // 注入自己
- @Autowired
- private OrderBizService orderBizService;
- @Override
- public void callBack() throws Exception {
- // 一系列的邏輯
- // 需要事務(wù)操作更新訂單和用戶金額
- orderBizService.updateOrderStatusAndUserBalance();
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void updateOrderStatusAndUserBalance() throws Exception {
- // 內(nèi)部是事務(wù)邏輯
- }
- }
是不是發(fā)現(xiàn)很神奇的事情,事務(wù)生效了。
其實(shí)這里注入自己,其實(shí)是注入的一個(gè)代理對(duì)象,調(diào)事務(wù),也是調(diào)的代理對(duì)象的事務(wù),所以事務(wù)生效。
Spring 事務(wù)失效原因:
事務(wù)只能應(yīng)用到 public 方法上才會(huì)有效;事務(wù)需要從外部調(diào)用,Spring 自調(diào)用會(huì)失效;建議事務(wù)注解 @Transactional 一般添加在實(shí)現(xiàn)類上。
異步的自注入
發(fā)現(xiàn) @Transactional 注解可以自注入解決事務(wù)失效的問題,在某次開發(fā)中,自然而然想到 @Async 異步是不是也可以自注入解決循環(huán)依賴的問題。
NO, NO, NO……
事實(shí)告訴我們是不可以的!
從錯(cuò)誤開始著手:
拋出異常部分 doCreateBean
開始往上面反推 exposedObject == bean 是這一塊出了問題。
也就是說(shuō)異步的時(shí)候,再次從二級(jí)緩存中獲取的和初始的不相同。
Object earlySingletonReference = getSingleton(beanName, false);
從二級(jí)緩存再次獲取 Bean
這一次獲取的時(shí)候發(fā)現(xiàn)不同所以報(bào)錯(cuò)。
那就開始 Debug, 按照循環(huán)依賴的邏輯,執(zhí)行到 populateBean 時(shí),屬性賦值,發(fā)現(xiàn)有依賴自己,此時(shí)會(huì)創(chuàng)建自己。
執(zhí)行 singleton.getObject 方法
getEarlyBeanReference
getBeanPostProcessors()
而此時(shí)執(zhí)行 getEarlyBeanReference 先判斷 InfrastructureAdvisorAutoProxyCreator true 調(diào)用 wrapIfNecessary 判斷是否生成一個(gè)代理對(duì)象,這里并沒有生成代理對(duì)象。
然后開始執(zhí)行異步的 AsyncAnnotationBeanPostProcessor 判斷為 false。所以沒有執(zhí)行異步的生成代理對(duì)象邏輯。
那就繼續(xù)往下看
到這一步還是正常的
進(jìn)入到 initializeBean 的邏輯,有一部分叫做 applyBeanPostProcessorsAfterInitialization
方面小伙伴搜索,所以貼出來(lái)代碼關(guān)鍵字。IDEA 使用 ? + Shift + F 搜索。
applyBeanPostProcessorsAfterInitialization
循環(huán)執(zhí)行后置處理器:
發(fā)現(xiàn)執(zhí)行完 AsyncAnnotationBeanPostProcessor 這個(gè) PostProcessor 后,對(duì)象被改變了。從而導(dǎo)致二級(jí)緩存和當(dāng)前的 Bean 不同。
以上也就是為什么 @Async 自調(diào)用不可以,因?yàn)樵诤竺娉跏蓟A段被代理修改了對(duì)象。
@Transactional 為什么可以呢?
getEarlyBeanReference
getBeanPostProcessors()
先判斷 InfrastructureAdvisorAutoProxyCreator true 生成一個(gè)代理對(duì)象。
生成代理對(duì)象
事務(wù)的處理器 PersistenceExceptionTranslationPostProcessor 也沒有執(zhí)行。
繼續(xù) Debug 關(guān)注 applyBeanPostProcessorsAfterInitialization
執(zhí)行結(jié)束,發(fā)現(xiàn) Bean 沒有發(fā)生改變。
總結(jié)
- @Transactional: 是在循環(huán)依賴從二級(jí)緩存升到三級(jí)緩存的時(shí)候已經(jīng)生成了代理對(duì)象。
- @Async: 是在初始化階段(initializeBean)去生成代理對(duì)象。然后 @Async 導(dǎo)致后面判斷 exposedObject == bean 為 false ,從而拋出異常。
自注入
可以看出圖中有兩處會(huì)執(zhí)行 BeanPostProcessor :
- 在 singletonFactory.getObject 時(shí),如果是 SmartInstantiationAwareBeanPostProcessor 的子類會(huì)執(zhí)行 getEarlyBeanReference 方法。
- 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 時(shí)會(huì)執(zhí)行所有 BeanPostProcessor 的 postProcessAfterInitialization 的方法。
也有其他的地方在執(zhí)行后置處理器,比如 applyBeanPostProcessorsBeforeInitialization ,只不過這里關(guān)注這倆處。
而這兩處都有可能生成代理對(duì)象, @Transactional 是在 getEarlyBeanReference 處生成的代理對(duì)象,所以后面判斷 Bean 是否被改變時(shí)為 true,而 @Async 是在后面異步生成了代理對(duì)象,所以判斷不通過。
本文轉(zhuǎn)載自微信公眾號(hào)「程序員小航」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序員小航公眾號(hào)。