spring事務管理的一些注意點
總結一些自己最近在使用spring事務管理時碰到的一些注意點
一、關於目標物件內部方法自我呼叫時的一些情形和存在的問題
1、情形1:只給b方法上加事務註解,a方法上不加
目標類的介面和實現程式碼示例:
public interface AService {
public void a();
public void b();
}
@Service()
public class AServiceImpl implements AService{
public void a() {
this.b();
}
@Transactional(rollbackFor={Exception.class})
public void b() {
insert();
update();
}
}
只要給目標類AServiceImpl的某個方法加上註解@Transactional,spring就會為目標類生成對應的代理類,以後呼叫AServiceImpl中的所有方法都會先走代理類(即使呼叫未加事務註解的方法a,也會走代理類),即在通過getBean(“AServiceImpl”)獲得的業務類時,實際上得到的是一個代理類,假設這個類叫做AServiceImplProxy ,spring為AServiceImpl生成的代理類類似於如下程式碼:
public class AServiceImplProxy implements AService{
public void a() {
//反射呼叫目標類的a方法
}
public void b() {
//啟動事務的程式碼
//反射呼叫目標類的b方法
//事務提交的程式碼
}
}
由於目標類中只有b方法加入了事務管理,所以代理類中只為b方法加入了橫切事務邏輯,spring事務管理的本質是通過aop為目標類生成動態代理類,並在需要進行事務管理的方法中加入事務管理的橫切邏輯程式碼(如AServiceImplProxy中的b方法所示)。
呼叫getBean(“AServiceImpl”).a()時,實際上執行的是AServiceImplProxy.a(),代理類的a方法會通過反射呼叫目標類的a方法, 再在目標類的a方法中呼叫b方法,故最終a中呼叫的b方法是來自於AServiceImpl中的b方法,AServiceImpl的b方法並沒有橫切事務邏輯程式碼(切記:事務邏輯程式碼在代理類中,@Transactional只是標記此方法在代理類中要加入事務邏輯程式碼)。所以呼叫a方法時,b方法的事務會失效。
其實,在proxy物件與目標物件之間還有一個InvocationHandler物件(以jdk動態代理為例),真正的橫切邏輯是放到InvocationHandler物件中的,呼叫邏輯分離到InvocationHandler中主要是為了構造出具有通用性和簡單性的代理類,此處為了簡化處理過程,統一放到代理物件中來說明,動態代理簡化的呼叫關係圖如下:
aop中存在方法巢狀呼叫時,相應的呼叫過程式列圖如下:
2、情形2:給a方法加事務註解,b方法上加或不加
對1中的程式碼做修改,為a方法也加上事務註解:
@Service()
public class AServiceImpl implements AService{
@Transactional(rollbackFor={Exception.class})
public void a() {
this.b();
}
@Transactional(rollbackFor={Exception.class})
public void b() {
insert();
update();
}
}
此時生成的代理類類似如下程式碼:
public class AServiceImplProxy implements AService{
public void a() {
//啟動事務的程式碼
//反射呼叫目標類的a方法
//事務提交的程式碼
}
public void b() {
//啟動事務的程式碼
//反射呼叫目標類的b方法
//事務提交的程式碼
}
}
即為a和b都加入了事務橫切邏輯。在這種情況下,呼叫順序還和1中情形類似,區別在於在反射呼叫目標物件的a方法前,會對a方法開啟事務管理,雖然呼叫的b方法還是目標物件中沒有加事務邏輯的程式碼,spring卻會把b合併到a的事務中去,此時相當於只有一個事務。
如果再將目標類程式碼改為:
@Service()
public class AServiceImpl implements AService{
@Transactional(rollbackFor={Exception.class})
public void a() {
this.b();
}
public void b() {
insert();
update();
}
}
即只在a上加事務控制,由於b會合併到a的事務中,所以b中的邏輯也可以被事務管理。
由於a和b都合併到了a的事務中,所以這種情形下事務傳遞規則不適用。代理類中加了事務邏輯的b方法永遠不會被呼叫。
那麼問題來了,如果我想讓b也執行自己的事務邏輯,即呼叫b時執行代理類中b方法的事務邏輯,該怎麼辦?
修改目標類中的a方法:
@Transactional(rollbackFor={Exception.class})
public void a() {
((AService) AopContext.currentProxy()).b();
//即呼叫AOP代理物件的b方法即可執行事務切面進行事務增強
}
這時,就會強制要求呼叫代理類中的b方法,從而開啟b上的事務,此時b事務上標註的事務傳遞規則也就可以生效了,詳情參見:http://jinnianshilongnian.iteye.com/blog/1487235
個人覺得這種方法不太好,會汙染業務邏輯程式碼,使程式碼變複雜。
還有一種辦法就是介面下沉,把b方法分離到另一個介面中,從根源上避免目標物件內部方法自我呼叫。
二、try catch的問題
有時需要在業務邏輯程式碼中顯式try catch包裹事務程式碼,以便在出現異常時進行一些別的處理。
目標類的介面和實現示例程式碼如下:
public interface AService {
public void a();
}
@Service()
public class AServiceImpl implements AService{
@Transactional(rollbackFor={Exception.class})
public void a() {
try{
insert();
update();
}catch(Exception e){
}
}
}
自己在程式碼中顯式捕獲異常會導致spring事務回滾失效,原因:spring事務是通過aop捕獲到異常後再執行回滾,如果業務程式碼中顯式捕獲了異常,會導致spring捕獲不到,回滾自然失敗。
有如下幾種解決辦法:
(1)業務程式碼catch住異常後重新丟擲,如:
public void a() throws Exception{
try{
insert();
update();
}catch(Exception e){
throw new Exception(e);
}
}
不足是本方法的呼叫端也必須顯式捕獲異常。
(2)使用程式設計式事務顯式回滾:
public void a() {
try{
insert();
update();
}catch(Exception e){
//顯式回滾
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
不足是事務控制程式碼會侵入業務程式碼,也正是因為程式設計式事務管理會侵入業務邏輯程式碼,所以才有了申明式事務管理。
(3)介面下沉,將需要事務控制的程式碼分到另一個介面方法中,如:
public interface BService {
public void a();
public void b();
}
@Service()
public class BServiceImpl implements BService{
@Transactional(rollbackFor={Exception.class})
public void b() {
insert();
update();
}
}
相應的呼叫端a方法中變為:
public void a() throws Exception{
try{
bService.b();
}catch(Exception e){
throw new Exception(e);
}
}
相關文章
- Spring事務需要注意的幾個點Spring
- 注意Spring事務這一點,避免出現大事務Spring
- @Transactional spring 配置事務 注意事項Spring
- Spring的事務管理(二)宣告式事務管理Spring
- Spring的事務管理Spring
- Spring宣告式事務注意點,以及不生效情況Spring
- Spring 事務管理Spring
- Spring事務管理Spring
- Spring 中的事務管理Spring
- Spring系列.事務管理Spring
- Spring系列-事務管理Spring
- (四)Spring中的事務管理Spring
- Spring 事務管理高階應用難點剖析Spring
- Spring的事務管理入門:程式設計式事務管理(TransactionTemplate)Spring程式設計
- Spring事務管理總結Spring
- spring 事務管理機制Spring
- Spring事務管理全解析Spring
- Spring整合Hibernate的事務管理Spring
- 關於SPRING的事務管理_求助Spring
- Spring Cloud 微服務的那點事SpringCloud微服務
- Spring的事務管理(一) Spring事務管理的實現,事務的屬性(隔離級別,傳播行為,只讀)Spring
- 全面分析 Spring 的程式設計式事務管理及宣告式事務管理Spring程式設計
- spring宣告式事務管理配置Spring
- spring datasource 配置及事務管理Spring
- 關於spring事務原始碼的一些小理解Spring原始碼
- Spring非同步執行(@Async)2點注意事項Spring非同步
- Spring 事務管理高階應用難點剖析: 第 1 部分Spring
- Spring 事務管理高階應用難點剖析: 第 2 部分Spring
- Spring 事務管理高階應用難點剖析: 第 3 部分Spring
- 基於Spring中的事務管理機制Spring
- 解析Spring Boot中的事務管理機制Spring Boot
- Spring事務管理:非常規指南 - marcobehlerSpring
- Spring 程式設計式事務管理Spring程式設計
- 分散式鎖和spring事務管理分散式Spring
- Spring事務管理(詳解+例項)Spring
- Spring事務管理—aop:pointcut expression解析SpringExpress
- 使用Spring Boot實現事務管理Spring Boot
- spring事務管理原始碼分析(二)事務處理流程分析Spring原始碼