Spring 事務、異步和循環依賴有什麼關係?

前言

在循環依賴中有一種循環依賴,就是自注入:本身依賴本身。java

事務的自注入

Spring 自調用事務失效,你是怎麼解決的? 有小夥伴提出能夠本身注入本身來解決事務失效。緩存

具體使用方式以下:app

@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 {
        // 內部是事務邏輯
    }
}

是否是發現很神奇的事情,事務生效了。異步

其實這裏注入本身,實際上是注入的一個代理對象,調事務,也是調的代理對象的事務,因此事務生效。ide

Spring 事務失效緣由:

事務只能應用到 public 方法上纔會有效;
事務須要從外部調用,Spring 自調用會失效;
建議事務註解 @Transactional 通常添加在實現類上。post

異步的自注入

發現 @Transactional 註解能夠自注入解決事務失效的問題,在某次開發中,天然而然想到 @Async 異步是否是也能夠自注入解決循環依賴的問題。spa

NO, NO, NO……代理

事實告訴咱們是不能夠的!code

從錯誤開始着手:對象

拋出異常部分 doCreateBean

開始往上面反推 exposedObject == bean 是這一塊出了問題。

也就是說異步的時候,再次從二級緩存中獲取的和初始的不相同。

Object earlySingletonReference = getSingleton(beanName, false);

從二級緩存再次獲取 Bean

這一次獲取的時候發現不一樣因此報錯。

那就開始 Debug, 按照循環依賴的邏輯,執行到 populateBean 時,屬性賦值,發現有依賴本身,此時會建立本身。

執行 singleton.getObject 方法

getEarlyBeanReference

getBeanPostProcessors()

而此時執行 getEarlyBeanReference 先判斷 InfrastructureAdvisorAutoProxyCreator true 調用 wrapIfNecessary 判斷是否生成一個代理對象,這裏並無生成代理對象。

而後開始執行異步的 AsyncAnnotationBeanPostProcessor 判斷爲 false。因此沒有執行異步的生成代理對象邏輯。

那就繼續往下看

到這一步仍是正常的

進入到 initializeBean 的邏輯,有一部分叫作 applyBeanPostProcessorsAfterInitialization

方面小夥伴搜索,因此貼出來代碼關鍵字。IDEA 使用 ⌘ + Shift + F 搜索。

applyBeanPostProcessorsAfterInitialization

循環執行後置處理器:

發現執行完 AsyncAnnotationBeanPostProcessor 這個 PostProcessor 後,對象被改變了。從而致使二級緩存和當前的 Bean 不一樣。

以上也就是爲何 @Async 自調用不能夠,由於在後面初始化階段被代理修改了對象。

@Transactional 爲何能夠呢?

getEarlyBeanReference

getBeanPostProcessors()

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

生成代理對象

事務的處理器 PersistenceExceptionTranslationPostProcessor 也沒有執行。

繼續 Debug 關注 applyBeanPostProcessorsAfterInitialization

執行結束,發現 Bean 沒有發生改變。

總結

  • @Transactional: 是在循環依賴從二級緩存升到三級緩存的時候已經生成了代理對象。
  • @Async: 是在初始化階段(initializeBean)去生成代理對象。而後 @Async 致使後面判斷 exposedObject == bean 爲 false ,從而拋出異常。

自注入

能夠看出圖中有兩處會執行 BeanPostProcessor :

  1. 在 singletonFactory.getObject 時,若是是 SmartInstantiationAwareBeanPostProcessor 的子類會執行 getEarlyBeanReference 方法。
  2. 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 時會執行全部 BeanPostProcessor 的 postProcessAfterInitialization 的方法。
也有其餘的地方在執行後置處理器,好比 applyBeanPostProcessorsBeforeInitialization ,只不過這裏關注這倆處。

而這兩處都有可能生成代理對象, @Transactional 是在 getEarlyBeanReference 處生成的代理對象,因此後面判斷 Bean 是否被改變時爲 true,而 @Async 是在後面異步生成了代理對象,因此判斷不經過。

至此,分析完畢,錯誤之處,歡迎指正。

相關推薦

相關文章
相關標籤/搜索