深入理解spring如何管理事務(wù)
Spring 事務(wù)管理概述
最先需要明確一點(diǎn)的是,事務(wù)只是數(shù)據(jù)庫(kù)中的一個(gè)概念,我們的java程序和數(shù)據(jù)庫(kù)進(jìn)行連接,java程序只是作為數(shù)據(jù)庫(kù)的客戶端,本質(zhì)是通過(guò)網(wǎng)絡(luò)連接對(duì)象connection來(lái)間接的操作數(shù)據(jù)庫(kù)對(duì)sql進(jìn)行事務(wù)開(kāi)啟,提交或者回滾。在 Spring 中,事務(wù)管理通常分為兩種方式:編程式事務(wù)和聲明式事務(wù)。其中,聲明式事務(wù)(基于注解 @Transactional)是最常用、最直觀的做法。本質(zhì)上,Spring 通過(guò) AOP(面向切面編程) 或 代理機(jī)制 來(lái)攔截方法調(diào)用,在方法執(zhí)行前后自動(dòng)開(kāi)啟、提交或回滾事務(wù)。
1. Spring 事務(wù)管理的整體架構(gòu)
1.1 PlatformTransactionManager接口
- Spring 為各種數(shù)據(jù)訪問(wèn)框架提供了不同實(shí)現(xiàn)的 PlatformTransactionManager,例如:
DataSourceTransactionManager(JDBC)JpaTransactionManager(JPA/Hibernate)HibernateTransactionManager(Hibernate)JpaTransactionManager(JPA)以及其他第三方框架的事務(wù)管理器。
- 這些事務(wù)管理器對(duì)底層資源(數(shù)據(jù)庫(kù)連接、Session 等)進(jìn)行事務(wù)控制,Spring 通過(guò)它們來(lái)統(tǒng)一管理事務(wù)的開(kāi)啟、提交與回滾。
1.2 事務(wù)配置(Transaction Configuration)
- 通過(guò)注解
@EnableTransactionManagement或者在 XML 中聲明<tx:annotation-driven>,開(kāi)啟 Spring 對(duì)聲明式事務(wù)的支持。 - 配置好對(duì)應(yīng)的
PlatformTransactionManagerBean,讓 Spring 知道使用哪一種事務(wù)管理器。
1.3 聲明式事務(wù)(@Transactional)
- Spring 在運(yùn)行時(shí)掃描被
@Transactional注解標(biāo)記的類(lèi)或方法,自動(dòng)為其生成 AOP 代理。 - 當(dāng)外部調(diào)用該方法時(shí),會(huì)先進(jìn)入 事務(wù)切面,根據(jù)注解信息決定是否開(kāi)啟事務(wù),執(zhí)行完畢后再根據(jù)方法執(zhí)行結(jié)果決定提交還是回滾。
1.4 事務(wù)增強(qiáng)(Advice)
- 事務(wù)的開(kāi)啟、提交、回滾等邏輯,就封裝在 Spring 提供的事務(wù)增強(qiáng)(
TransactionInterceptor)中,通過(guò) AOP 動(dòng)態(tài)攔截目標(biāo)方法調(diào)用。
2. Spring 事務(wù)管理的關(guān)鍵流程
以下主要針對(duì) 聲明式事務(wù)(基于注解)進(jìn)行說(shuō)明:
2.1 Bean 初始化階段
- Spring 在啟動(dòng)時(shí),會(huì)掃描所有的 Bean,看它們是否使用了
@Transactional注解。 - 如果某個(gè)類(lèi)或方法使用了
@Transactional,則由 Spring AOP 或 AspectJ 生成一個(gè)代理對(duì)象來(lái)代替原始對(duì)象。 - 代理對(duì)象內(nèi)部持有對(duì)真實(shí)對(duì)象的引用,同時(shí)嵌入事務(wù)管理邏輯。
2.2 方法調(diào)用攔截(AOP 代理)
- 當(dāng)外部代碼調(diào)用該 Bean 的某個(gè)帶有
@Transactional的方法時(shí),實(shí)際上先調(diào)用到 代理對(duì)象。 - 代理對(duì)象 會(huì)進(jìn)行一系列判斷,包括:
- 讀取
@Transactional的屬性(如propagation、isolation、timeout、readOnly等); - 判斷當(dāng)前線程是否已經(jīng)存在事務(wù)(如果有,則根據(jù)
propagation屬性決定如何處理); - 如果沒(méi)有事務(wù)或者需要新建事務(wù),則調(diào)用
PlatformTransactionManager的getTransaction(...)開(kāi)啟一個(gè)事務(wù)。
- 讀取
2.3 執(zhí)行目標(biāo)方法
在事務(wù)已開(kāi)啟(或已決定加入現(xiàn)有事務(wù))的前提下,代理對(duì)象通過(guò)反射調(diào)用目標(biāo) Bean 的真實(shí)方法。目標(biāo)方法中執(zhí)行數(shù)據(jù)庫(kù)操作(或其他資源操作),如果拋出異常,Spring 會(huì)捕獲并根據(jù) rollback rules(例如:RuntimeException、Error 默認(rèn)回滾等)決定是否回滾。
2.4 提交或回滾事務(wù)
- 如果目標(biāo)方法正常結(jié)束,Spring 在退出代理方法前,會(huì)調(diào)用
PlatformTransactionManager的commit(...)方法,提交事務(wù); - 如果出現(xiàn)了異常且符合回滾條件,Spring 調(diào)用
PlatformTransactionManager的rollback(...)方法,回滾事務(wù); - 此后,方法調(diào)用才真正返回給調(diào)用者。
2.5 釋放資源
- 事務(wù)提交或回滾之后,底層數(shù)據(jù)庫(kù)連接、Session 等資源會(huì)被釋放或歸還到連接池。
- Spring 也會(huì)在內(nèi)部進(jìn)行相應(yīng)的清理操作,確保線程上下文不再持有錯(cuò)誤的事務(wù)信息。
3. 事務(wù)屬性與配置
在使用 @Transactional 注解時(shí),可以指定多個(gè)屬性來(lái)控制事務(wù)行為,比如:
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = 30,
readOnly = false,
rollbackFor = {Exception.class},
noRollbackFor = {CustomException.class}
)
public void doSomething() {
// ...
}propagation - 事務(wù)傳播級(jí)別
常見(jiàn)值:REQUIRED、REQUIRES_NEW、SUPPORTS、MANDATORY、NESTED 等,決定當(dāng)前方法是否需要在已有事務(wù)中執(zhí)行,或新開(kāi)一個(gè)事務(wù)等。
isolation - 事務(wù)隔離級(jí)別
如 READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE,決定事務(wù)間讀寫(xiě)數(shù)據(jù)的可見(jiàn)性,防止臟讀、不可重復(fù)讀、幻讀等。
timeout - 超時(shí)時(shí)間
指定事務(wù)執(zhí)行的最大時(shí)間(秒),超過(guò)時(shí)間未完成則拋出超時(shí)異常并回滾。
readOnly - 只讀標(biāo)志
指示該事務(wù)主要用于查詢操作,可能在某些情況下允許數(shù)據(jù)庫(kù)做額外優(yōu)化;某些數(shù)據(jù)庫(kù)會(huì)忽略此配置。
rollbackFor / noRollbackFor - 回滾策略
指定哪些異常拋出時(shí)應(yīng)回滾或不回滾;默認(rèn)對(duì) 運(yùn)行時(shí)異常 和 錯(cuò)誤 回滾,對(duì)編譯異常 不回滾。
4. 實(shí)際開(kāi)發(fā)中事務(wù)的常見(jiàn)問(wèn)題
同類(lèi)方法相互調(diào)用失效
如果在方法 A 內(nèi)部調(diào)用方法 B,而 B 也使用了 @Transactional,但這兩者都屬于同一個(gè)類(lèi),則 B 的事務(wù)不會(huì)被代理攔截,導(dǎo)致事務(wù)注解失效??梢酝ㄟ^(guò)將 B 提取到另一個(gè) Bean 或使用 AspectJ 等方式解決。
異常類(lèi)型導(dǎo)致事務(wù)不回滾
Spring 默認(rèn)對(duì) RuntimeException 或 Error 回滾,而對(duì)受檢異常(Checked Exception)不回滾,需要在注解中顯式指定 rollbackFor。不小心拋出了“錯(cuò)誤類(lèi)型”的異常,就可能導(dǎo)致事務(wù)回滾意外。
讀寫(xiě)操作混用導(dǎo)致性能或數(shù)據(jù)一致性問(wèn)題@Transactional(readOnly = true) 僅作提示,不會(huì)強(qiáng)制阻止寫(xiě)操作。如果在此事務(wù)中執(zhí)行了寫(xiě)操作或忘記設(shè)置 readOnly = false,就可能引發(fā)數(shù)據(jù)不一致或性能問(wèn)題。
事務(wù)傳播屬性配置不當(dāng)
如果不小心將一個(gè)需要新事務(wù)的操作配置為 Propagation.REQUIRED 而不是 REQUIRES_NEW,或者反之,則會(huì)導(dǎo)致事務(wù)邊界不符預(yù)期,進(jìn)而出現(xiàn)臟數(shù)據(jù)、鎖競(jìng)爭(zhēng)等問(wèn)題。
大事務(wù)導(dǎo)致鎖競(jìng)爭(zhēng)
在實(shí)際項(xiàng)目里,如果單次事務(wù)持續(xù)時(shí)間過(guò)長(zhǎng),可能會(huì)長(zhǎng)時(shí)間占用數(shù)據(jù)庫(kù)鎖,導(dǎo)致鎖競(jìng)爭(zhēng)或死鎖;需注意將大事務(wù)拆分或優(yōu)化為多次小事務(wù)。
多線程/異步場(chǎng)景下無(wú)法共享事務(wù)
Spring 的默認(rèn)事務(wù)機(jī)制基于 ThreadLocal 綁定連接,異步任務(wù)或多線程無(wú)法復(fù)用同一事務(wù),如果你在一個(gè)方法里開(kāi)啟多線程進(jìn)行并發(fā)數(shù)據(jù)庫(kù)操作,子線程無(wú)法直接繼承主線程的事務(wù)上下文,會(huì)拿不到同一個(gè) Connection。
5.spring聲明式事務(wù)執(zhí)行過(guò)程示例圖

6.總結(jié)
核心原理
Spring 通過(guò) AOP 代理 攔截帶有 @Transactional 的方法,在方法執(zhí)行前后,根據(jù)注解屬性和底層 PlatformTransactionManager 自動(dòng)開(kāi)啟、提交或回滾事務(wù)。
關(guān)鍵組件
@Transactional注解- 事務(wù)切面(
TransactionInterceptor) PlatformTransactionManagerTransactionSynchronizationManager(這是 Spring 用于管理“線程上下文”的關(guān)鍵工具類(lèi),記錄當(dāng)前線程關(guān)聯(lián)的事務(wù)資源,如Connect。
同一事務(wù)內(nèi),所有數(shù)據(jù)庫(kù)操作都共享此 Connection)
事務(wù)配置
可自定義傳播級(jí)別、隔離級(jí)別、超時(shí)、回滾策略等,以滿足不同的業(yè)務(wù)需要。
到此這篇關(guān)于spring如何管理事務(wù)的文章就介紹到這了,更多相關(guān)spring管理事務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于spring cloud多個(gè)消費(fèi)端重復(fù)定義feign client的問(wèn)題
這篇文章主要介紹了spring cloud多個(gè)消費(fèi)端重復(fù)定義feign client的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Mybatis?Plus?中的LambdaQueryWrapper示例詳解
這篇文章主要介紹了Mybatis?Plus?中的LambdaQueryWrapper,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
springBoot+mybatis-plus實(shí)現(xiàn)監(jiān)聽(tīng)mysql數(shù)據(jù)庫(kù)的數(shù)據(jù)增刪改
mybatis-plus技術(shù)是簡(jiǎn)化了繁瑣的代碼操作,把增刪改查的語(yǔ)句都內(nèi)置了,直接調(diào)用就可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)的增刪改查了,這篇文章主要給大家介紹了關(guān)于springBoot+mybatis-plus實(shí)現(xiàn)監(jiān)聽(tīng)mysql數(shù)據(jù)庫(kù)數(shù)據(jù)增刪改的相關(guān)資料,需要的朋友可以參考下2024-01-01
springboot應(yīng)用訪問(wèn)zookeeper的流程
這篇文章主要介紹了springboot應(yīng)用訪問(wèn)zookeeper的流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Java實(shí)現(xiàn)產(chǎn)生隨機(jī)字符串主鍵的UUID工具類(lèi)
這篇文章主要介紹了Java實(shí)現(xiàn)產(chǎn)生隨機(jī)字符串主鍵的UUID工具類(lèi),涉及java隨機(jī)數(shù)與字符串遍歷、轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
java自帶命令行工具jmap、jhat與jinfo的使用實(shí)例代碼詳解
本篇文章主要通過(guò)代碼實(shí)例對(duì)java自帶命令行工具jmap、jhat與jinfo的使用做出了詳解,需要的朋友可以參考下2017-04-04
通過(guò)Spring層面進(jìn)行事務(wù)回滾的實(shí)現(xiàn)
本文主要介紹了通過(guò)Spring層面進(jìn)行事務(wù)回滾的實(shí)現(xiàn),包括聲明式事務(wù)和編程式事務(wù),具有一定的參考價(jià)值,感興趣的可以了解一下2025-04-04
java:java.lang.ExceptionInInitializerError報(bào)錯(cuò)解決過(guò)程
這篇文章主要給大家介紹了關(guān)于java:java.lang.ExceptionInInitializerError報(bào)錯(cuò)的解決過(guò)程,java.lang.ExceptionInInitializerError 是一個(gè)異常,表示在初始化一個(gè)類(lèi)的靜態(tài)變量或靜態(tài)塊時(shí)發(fā)生了錯(cuò)誤,需要的朋友可以參考下2023-10-10
簡(jiǎn)單了解java類(lèi)型轉(zhuǎn)換常見(jiàn)的錯(cuò)誤
這篇文章主要介紹了簡(jiǎn)單了解java類(lèi)型轉(zhuǎn)換常見(jiàn)的錯(cuò)誤,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04

