在 Spring 框架中,@Transactional 注解作為一種聲明式事務管理的關鍵機制,其背后的工作原理遠比簡單的 AOP(面向切面編程)和 ThreadLocal 存儲更為細膩。該注解的實現核心在于 Spring 的 TransactionInterceptor(事務攔截器)以及它如何與 Spring 的代理機制、TransactionManager(事務管理器)協同工作,來確保事務的開啟、提交或回滾等操作得以正確執行。
當 Spring 容器初始化時,會通過 AnnotationTransactionAttributeSource 掃描并識別出所有標有 @Transactional 的方法。這些方法在被調用前,Spring 會根據配置(如基于接口或類的代理)為它們創建動態代理對象。如果是基于接口的代理,則使用 JDK Dynamic Proxy;如果是基于類的,則采用 CGLIB。這個代理對象會在目標方法調用前后插入事務處理邏輯。
一切始于 Spring 容器的 Bean 創建和初始化過程。Spring 通過一系列的 BeanPostProcessor 接口實現類來增強 Bean 的功能,其中與事務管理密切相關的便是 AbstractAutoProxyCreator的子類,如 AnnotationAwareAspectJAutoProxyCreator。這個類負責掃描并創建代理對象,以便于在運行時織入諸如事務管理這樣的切面邏輯。
總結下就是,Spring 通過 Bean 的后置處理器在容器初始化階段自動檢測帶有 @Transactional 注解的類和方法,并通過動態代理機制為這些組件創建代理對象。代理對象在方法調用時,通過 TransactionInterceptor 這一核心組件,根據注解配置的事務屬性來管理事務生命周期,確保事務邏輯的無縫集成。
TransactionInterceptor 實現了 Spring 的 MethodInterceptor 接口,這意味著它能夠攔截方法調用,并在調用前后執行額外的邏輯,即事務管理邏輯。其核心職責包括:
在事務上下文中執行實際的目標方法。此時,如果目標方法內部拋出了異常,這個異常會被暫存以供后續處理。
方法調用后:
異常處理: 如果方法執行過程中拋出了異常,TransactionInterceptor 會捕獲到這個異常,并根據異常類型及事務屬性決定是否需要回滾事務。通常,非檢查型異常(繼承自 RuntimeException 的異常)會導致事務回滾,而檢查型異常則不會,除非事務屬性特別指定了回滾規則。
事務提交或回滾: 如果方法正常結束,或者按事務屬性應該提交事務的情況下,事務管理器會提交事務。相反,如果需要回滾,則執行回滾操作。
資源清理: 在事務結束后,確保所有資源被正確釋放,比如關閉數據庫連接等。
整個過程中,動態代理扮演著關鍵角色。無論是 JDK 動態代理還是 CGLIB 代理,它們都是在調用真正業務方法之前插入事務攔截邏輯的橋梁。當客戶端代碼調用代理對象上的方法時,實際上是調用了由 TransactionInterceptor 所控制的代理邏輯,從而透明地在業務邏輯執行前后管理事務。
通過這種方式,開發者無需在每個需要事務管理的方法中手動編寫開啟、提交或回滾事務的代碼,極大地簡化了開發復雜度,提高了代碼的可維護性和可讀性。
當 @Transactional 標記的方法內部啟動新的線程時,問題就顯現了。前面提到,事務攔截使用了 TransactionInterceptor,而 TransactionInterceptor 內部用到了 TransactionSynchronizationManager,TransactionSynchronizationManager 使用 ThreadLocal 來存儲事務相關的資源信息,如數據庫連接、JMS 會話等。這意味著每個線程都有其獨立的事務上下文,確保了事務信息在線程間的隔離。
換句話說,Spring 管理的事務上下文是基于調用線程的,新線程并沒有繼承原線程的 TransactionSynchronizationManager 中的事務上下文。因此,新線程執行的數據庫操作實際上是在無事務管理的環境下進行的,這就導致事務失效。
那如果非要用多線程怎么辦呢?這個時候可以使用編程式事務,首先設置一個全局變量 Boolean,默認是可以提交的 true,在子線程,通過編程式事務的方式去開啟事務,然后插入數據,插入完成后,事務先不提交,同時通知主線程,我準備好了,進入等待狀態。如果子線程出現異常,那就通知主線程,我這邊發生異常,然后自己回滾。
最后主線程收集各個子線程的狀態,如果有一個線程出現問題,那么全局變量就設置為不可提交false,然后喚醒所有子線程,進行回滾。
這里涉及到線程同步可以利用閉鎖去實現;當主線程通知各個子線程提交事務的時候,子線程可能在提交的時候出錯了,最終導致數據不一致,那么這種時候可能就需要引入重試機制,有了重試機制后,又要去保證冪等性等等。
這一套方案下來大伙有沒有覺得眼熟,是不是就是分布式事務的處理思路了?
所以說,非要在事務中開啟多線程也沒問題,但是不建議這么做。
本文鏈接:http://www.tebozhan.com/showinfo-26-93354-0.html事務中存在多線程,怎么處理?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
下一篇: Fiddler:一個大名鼎鼎的私藏工具