Spring @Transactional 自調(diào)用問題深度解析及解決方案
Spring @Transactional 自調(diào)用問題深度解析
問題本質(zhì):自調(diào)用事務(wù)失效
當(dāng)類內(nèi)部的方法A調(diào)用同一個(gè)類的另一個(gè)帶有@Transactional注解的方法B時(shí),事務(wù)注解不會(huì)生效。這是因?yàn)镾pring的事務(wù)管理是基于AOP代理實(shí)現(xiàn)的,而自調(diào)用會(huì)繞過代理機(jī)制。
原理分析
1. Spring事務(wù)實(shí)現(xiàn)機(jī)制
Spring事務(wù)是通過動(dòng)態(tài)代理實(shí)現(xiàn)的,有兩種方式:
- JDK動(dòng)態(tài)代理:基于接口
- CGLIB代理:基于類繼承
// 原始調(diào)用流程(期望的事務(wù)流程) caller → 代理對(duì)象 → 目標(biāo)對(duì)象.methodB() // 自調(diào)用時(shí)的實(shí)際流程 caller → 目標(biāo)對(duì)象.methodA() → 目標(biāo)對(duì)象.methodB() [繞過代理]
2. 自調(diào)用問題示例
@Service
public class OrderService {
public void placeOrder(Order order) {
// 自調(diào)用導(dǎo)致事務(wù)失效
validateOrder(order);
// 其他業(yè)務(wù)邏輯...
}
@Transactional
public void validateOrder(Order order) {
// 數(shù)據(jù)庫驗(yàn)證操作...
}
}解決方案
方案1:注入自身代理(推薦)
@Service
public class OrderService {
@Autowired
private OrderService selfProxy; // 注入代理對(duì)象
public void placeOrder(Order order) {
selfProxy.validateOrder(order); // 通過代理調(diào)用
}
@Transactional
public void validateOrder(Order order) {
// 事務(wù)生效
}
}方案2:重構(gòu)代碼結(jié)構(gòu)
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderValidator orderValidator;
public void placeOrder(Order order) {
orderValidator.validate(order);
}
}
@Service
class OrderValidator {
@Transactional
public void validate(Order order) {
// 事務(wù)操作
}
}方案3:使用AspectJ模式(編譯時(shí)織入)
# application.properties spring.aop.proxy-target-class=true spring.aop.auto=false
技術(shù)深度:Spring事務(wù)代理機(jī)制
代理創(chuàng)建過程
- 容器啟動(dòng)時(shí)創(chuàng)建原始Bean
- 通過
AbstractAutoProxyCreator創(chuàng)建代理 - 對(duì)
@Transactional方法添加攔截器
事務(wù)攔截器調(diào)用棧
TransactionInterceptor.invoke() → MethodInvocation.proceed() → ReflectiveMethodInvocation.proceed() → 最終調(diào)用目標(biāo)方法
生產(chǎn)環(huán)境最佳實(shí)踐
統(tǒng)一事務(wù)邊界:
@Service
@Transactional // 類級(jí)別注解
public class OrderService {
public void placeOrder() {
// 所有public方法都默認(rèn)有事務(wù)
}
}事務(wù)監(jiān)控:
@Aspect
@Component
public class TransactionMonitor {
@Around("@annotation(transactional)")
public Object monitor(ProceedingJoinPoint pjp, Transactional transactional)
throws Throwable {
// 記錄事務(wù)開始/結(jié)束
}
}異常處理:
@Transactional(rollbackFor = {BusinessException.class, TechnicalException.class})
public void process() {
// 明確指定回滾異常類型
}常見誤區(qū)
私有方法加注解:
@Transactional // 無效!
private void internalMethod() {}final方法加注解:
@Transactional // CGLIB代理下無效!
public final void finalMethod() {}同類非事務(wù)方法調(diào)用事務(wù)方法:
public void methodA() {
methodB(); // 事務(wù)失效
}
@Transactional
public void methodB() {}性能考量
- 代理創(chuàng)建會(huì)增加啟動(dòng)時(shí)間
- 每個(gè)事務(wù)方法調(diào)用都有攔截開銷
- 長(zhǎng)事務(wù)會(huì)占用數(shù)據(jù)庫連接
到此這篇關(guān)于Spring @Transactional 自調(diào)用問題深度解析的文章就介紹到這了,更多相關(guān)Spring @Transactional 自調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(3)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-07-07
Myeclipse工程發(fā)布時(shí)端口占用問題的解決方法
這篇文章主要介紹了Myeclipse工程發(fā)布時(shí)端口占用問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
SpringBoot默認(rèn)包掃描機(jī)制與默認(rèn)配置文件詳解
這篇文章主要給大家詳細(xì)介紹了SpringBoot默認(rèn)包掃描機(jī)制的原理和示例,以及SpringBoot默認(rèn)配置文件介紹,文章通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
springboot如何開啟緩存@EnableCaching(使用redis)
在Spring Boot項(xiàng)目中集成Redis主要包括添加依賴到pom.xml、配置application.yml中的Redis連接參數(shù)、編寫配置類、在啟動(dòng)類上添加@EnableCaching注解以及測(cè)試接口的查詢和緩存驗(yàn)證等步驟,首先,需要在pom.xml中添加spring-boot-starter-data-redis依賴2024-11-11
idea sql的xml文件出現(xiàn)紅色警告符的處理方式
這篇文章主要介紹了idea sql的xml文件出現(xiàn)紅色警告符處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

