天天看點

spring事務管理的一些注意點一、關于目标對象内部方法自我調用時的一些情形和存在的問題二、try catch的問題

總結一些自己最近在使用spring事務管理時碰到的一些注意點

目标類的接口和實作代碼示例:

隻要給目标類aserviceimpl的某個方法加上注解@transactional,spring就會為目标類生成對應的代理類,以後調用aserviceimpl中的所有方法都會先走代理類(即使調用未加事務注解的方法a,也會走代理類),即在通過getbean("aserviceimpl")獲得的業務類時,實際上得到的是一個代理類,假設這個類叫做aserviceimplproxy ,spring為aserviceimpl生成的代理類類似于如下代碼:

由于目标類中隻有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中主要是為了構造出具有通用性和簡單性的代理類,此處為了簡化處理過程,統一放到代理對象中來說明,動态代理簡化的調用關系圖如下:

spring事務管理的一些注意點一、關于目标對象内部方法自我調用時的一些情形和存在的問題二、try catch的問題

aop中存在方法嵌套調用時,相應的調用過程式列圖如下:

spring事務管理的一些注意點一、關于目标對象内部方法自我調用時的一些情形和存在的問題二、try catch的問題

對1中的代碼做修改,為a方法也加上事務注解:

此時生成的代理類類似如下代碼:

即為a和b都加入了事務橫切邏輯。在這種情況下,調用順序還和1中情形類似,差別在于在反射調用目标對象的a方法前,會對a方法開啟事務管理,雖然調用的b方法還是目标對象中沒有加事務邏輯的代碼,spring卻會把b合并到a的事務中去,此時相當于隻有一個事務。

如果再将目标類代碼改為:

即隻在a上加事務控制,由于b會合并到a的事務中,是以b中的邏輯也可以被事務管理。

由于a和b都合并到了a的事務中,是以這種情形下事務傳遞規則不适用。代理類中加了事務邏輯的b方法永遠不會被調用。

那麼問題來了,如果我想讓b也執行自己的事務邏輯,即調用b時執行代理類中b方法的事務邏輯,該怎麼辦?

修改目标類中的a方法:

這時,就會強制要求調用代理類中的b方法,進而開啟b上的事務,此時b事務上标注的事務傳遞規則也就可以生效了,詳情參見:http://jinnianshilongnian.iteye.com/blog/1487235

個人覺得這種方法不太好,會污染業務邏輯代碼,使代碼變複雜。

還有一種辦法就是接口下沉,把b方法分離到另一個接口中,從根源上避免目标對象内部方法自我調用。

有時需要在業務邏輯代碼中顯式try catch包裹事務代碼,以便在出現異常時進行一些别的處理。

目标類的接口和實作示例代碼如下:

自己在代碼中顯式捕獲異常會導緻spring事務復原失效,原因:spring事務是通過aop捕獲到異常後再執行復原,如果業務代碼中顯式捕獲了異常,會導緻spring捕獲不到,復原自然失敗。

有如下幾種解決辦法:

(1)業務代碼catch住異常後重新抛出,如:

不足是本方法的調用端也必須顯式捕獲異常。

(2)使用程式設計式事務顯式復原:

不足是事務控制代碼會侵入業務代碼,也正是因為程式設計式事務管理會侵入業務邏輯代碼,是以才有了申明式事務管理。

(3)接口下沉,将需要事務控制的代碼分到另一個接口方法中,如:

相應的調用端a方法中變為: