Spring事務不生效問題
阿新 • • 發佈:2021-11-02
事務未生效可能造成嚴重的資料不一致性問題,因而保證事務生效至關重要。Spring事務是通過Spring aop實現的,所以不生效的本質問題是spring aop沒生效,或者說沒有代理成功,所以有必要了解下spring aop。
spring事務不生效場景
- 非public修飾方法
- private修飾
@Transactional
private void save(){...}
呼叫save方法,事務不生效 - protected修飾
@Transactional
protected void save(){...}
呼叫save方法,事務不生效
- private修飾
- final修飾的public法或類
- final修飾方法
@Transactional
public final void save(){...}
呼叫save方法,事務不生效 - final修飾類
@Transactional
public final class UserService{...}
呼叫UserService的方法,事務不生效
- final修飾方法
- 被代理物件內部方法呼叫
- 非事務方法呼叫內部事務方法
public void save(){
insert()
}
@Transactional
public void insert(){...}
此時呼叫save()方法,insert()上的事務不生效 - 事務方法呼叫內部事務方法
@Transactional
public void save(){
insert()
}
@Transactional
public void insert(){...}
此時呼叫save()方法,insert()上的事務不生效,save()方法正常呼叫時(非內部方法呼叫)事務生效
- 非事務方法呼叫內部事務方法
Spring aop的簡單介紹
spring aop支援動態代理(執行時代理)和靜態代理(編譯期織入)
-
基本概念
- target物件
目標物件,即想要代理的目標。 - proxy
代理物件,訪問目標物件的方法,需要通過代理物件,無法直接訪問目標物件。
代理物件可能允許,最終呼叫目標物件方法,也可能不允許。
- target物件
-
靜態代理(Aspectj)
1.該實現需要使用特殊編譯器,一般不使用,spring只是提供相應的整合實現。
2.使用動態代理已經可以完成80%以上的需求了。
3.本文也是基於動態代理的情況下討論事務不生效情況的。
4.因此在此不展開詳細討論。 -
動態代理(CGLIB&JDK)
- JDK動態代理
1.JDK動態代理通過實現相同介面實現代理(Proxy)
2.spring管理的Bean中是對應的是Proxy
3.Proxy物件中包含target物件
4.目標方法可以執行時,總是呼叫target的原生方法
5.事務aop預設publicMethodsOnly(該配置未提供直接修改-AnnotationTransactionAttributeSource) - CGLIB動態代理
1.CGLIB動態代理通過繼承目標類生成子類作為代理
2.spring管理的Bean中是對應的是Proxy
3.Proxy物件中包含target物件
4.目標方法可以執行時,總是呼叫target的原生方法
5.事務aop預設publicMethodsOnly(該配置未提供直接修改-AnnotationTransactionAttributeSource)
- JDK動態代理
Spring 事務不生效情況(即aop不生效情況),原因解析
- 無法代理
- 方法使用非public修飾(protected、private等等)
@Transactional
private void save(){...}
或
@Transactional
protected void save(){...}
spring 事務aop預設publicMethodsOnly,即只有public修飾的方法,會被代理,private和protected修飾的方法save()無法被代理,事務無效。 - 方法使用final修飾
@Transactional
public final void save(){...}
CGLIB動態代理通過生成子類的方式代理,final方法無法重寫 - 類使用final修飾
@Transactional
public final class UserService{...}
CGLIB動態代理通過生成子類的方式代理,final類無法生成子類
- 方法使用非public修飾(protected、private等等)
- 未通過proxy物件呼叫
- 非事務方法呼叫內部事務方法
public void save(){
insert()
}
@Transactional
public void insert(){...}
該情況隱含使用this關鍵字,因此被代理物件的內部事務方法insert()直接被呼叫,而不經過代理物件;
內部方法insert()上的事務無效。 - 事務方法呼叫內部事務方法
@Transactional
public void save(){
insert()
}
@Transactional
public void insert(){...}
該情況隱含使用this關鍵字,因此被代理物件的內部事務方法insert()直接被呼叫,而不經過代理物件;
內部方法insert()上的事務無效,但是事務方法save()正確呼叫時(通過代理物件呼叫),save()方法的事務生效。
- 非事務方法呼叫內部事務方法
除錯輔助
當無法通過程式碼分析確定是否有事務(aop是否代理成功)時,可以在開發環境除錯確認
- 事務AOP
在配置了expose-proxy=true時,呼叫靜態方法TransactionAspectSupport.currentTransactionStatus()可以獲取當前事務資訊;
該資訊儲存在ThreadLocal型別的執行緒變數;不存在時會拋異常,所以使用時需要try-catch。 - 其他AOP
在配置了expose-proxy=true時,呼叫靜態方法AopContext.currentProxy()可以獲取當前代理物件;該物件儲存在ThreadLocal型別的執行緒變數;不存在時會拋異常,所以使用時需要try-catch。