Spring @Transactional 自調(diào)用問(wèn)題深度解析及解決方案
Spring @Transactional 自調(diào)用問(wèn)題深度解析
問(wèn)題本質(zhì):自調(diào)用事務(wù)失效
當(dāng)類(lèi)內(nèi)部的方法A調(diào)用同一個(gè)類(lèi)的另一個(gè)帶有@Transactional注解的方法B時(shí),事務(wù)注解不會(huì)生效。這是因?yàn)镾pring的事務(wù)管理是基于A(yíng)OP代理實(shí)現(xiàn)的,而自調(diào)用會(huì)繞過(guò)代理機(jī)制。
原理分析
1. Spring事務(wù)實(shí)現(xiàn)機(jī)制
Spring事務(wù)是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的,有兩種方式:
- JDK動(dòng)態(tài)代理:基于接口
- CGLIB代理:基于類(lèi)繼承
// 原始調(diào)用流程(期望的事務(wù)流程) caller → 代理對(duì)象 → 目標(biāo)對(duì)象.methodB() // 自調(diào)用時(shí)的實(shí)際流程 caller → 目標(biāo)對(duì)象.methodA() → 目標(biāo)對(duì)象.methodB() [繞過(guò)代理]
2. 自調(diào)用問(wèn)題示例
@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ù)庫(kù)驗(yàn)證操作...
}
}解決方案
方案1:注入自身代理(推薦)
@Service
public class OrderService {
@Autowired
private OrderService selfProxy; // 注入代理對(duì)象
public void placeOrder(Order order) {
selfProxy.validateOrder(order); // 通過(guò)代理調(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)建過(guò)程
- 容器啟動(dòng)時(shí)創(chuàng)建原始Bean
- 通過(guò)
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 // 類(lèi)級(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ù)開(kāi)始/結(jié)束
}
}異常處理:
@Transactional(rollbackFor = {BusinessException.class, TechnicalException.class})
public void process() {
// 明確指定回滾異常類(lèi)型
}常見(jiàn)誤區(qū)
私有方法加注解:
@Transactional // 無(wú)效!
private void internalMethod() {}final方法加注解:
@Transactional // CGLIB代理下無(wú)效!
public final void finalMethod() {}同類(lèi)非事務(wù)方法調(diào)用事務(wù)方法:
public void methodA() {
methodB(); // 事務(wù)失效
}
@Transactional
public void methodB() {}性能考量
- 代理創(chuàng)建會(huì)增加啟動(dòng)時(shí)間
- 每個(gè)事務(wù)方法調(diào)用都有攔截開(kāi)銷(xiāo)
- 長(zhǎng)事務(wù)會(huì)占用數(shù)據(jù)庫(kù)連接
到此這篇關(guān)于Spring @Transactional 自調(diào)用問(wèn)題深度解析的文章就介紹到這了,更多相關(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ī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07
Myeclipse工程發(fā)布時(shí)端口占用問(wèn)題的解決方法
這篇文章主要介紹了Myeclipse工程發(fā)布時(shí)端口占用問(wèn)題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
SpringBoot默認(rèn)包掃描機(jī)制與默認(rèn)配置文件詳解
這篇文章主要給大家詳細(xì)介紹了SpringBoot默認(rèn)包掃描機(jī)制的原理和示例,以及SpringBoot默認(rèn)配置文件介紹,文章通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
springboot如何開(kāi)啟緩存@EnableCaching(使用redis)
在Spring Boot項(xiàng)目中集成Redis主要包括添加依賴(lài)到pom.xml、配置application.yml中的Redis連接參數(shù)、編寫(xiě)配置類(lèi)、在啟動(dòng)類(lèi)上添加@EnableCaching注解以及測(cè)試接口的查詢(xún)和緩存驗(yàn)證等步驟,首先,需要在pom.xml中添加spring-boot-starter-data-redis依賴(lài)2024-11-11
idea sql的xml文件出現(xiàn)紅色警告符的處理方式
這篇文章主要介紹了idea sql的xml文件出現(xiàn)紅色警告符處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

