這篇文章主要為大家展示了“Spring中@Transactional注解失效怎么辦”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Spring中@Transactional注解失效怎么辦”這篇文章吧。
成都創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站建設(shè)、網(wǎng)站制作與策劃設(shè)計(jì),枝江網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)十載,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:枝江等地區(qū)。枝江做網(wǎng)站價(jià)格咨詢:028-86922220
這幾天在項(xiàng)目里面發(fā)現(xiàn)我使用@Transactional注解事務(wù)之后,拋了異常居然不回滾。后來(lái)終于找到了原因。 如果你也出現(xiàn)了這種情況,可以從下面開(kāi)始排查。
一、特性
先來(lái)了解一下@Transactional注解事務(wù)的特性吧,可以更好排查問(wèn)題
1、service類標(biāo)簽(一般不建議在接口上)上添加@Transactional,可以將整個(gè)類納入spring事務(wù)管理,在每個(gè)業(yè)務(wù)方法執(zhí)行時(shí)都會(huì)開(kāi)啟一個(gè)事務(wù),不過(guò)這些事務(wù)采用相同的管理方式。
2、@Transactional 注解只能應(yīng)用到 public 可見(jiàn)度的方法上。 如果應(yīng)用在protected、private或者 package可見(jiàn)度的方法上,也不會(huì)報(bào)錯(cuò),不過(guò)事務(wù)設(shè)置不會(huì)起作用。
3、默認(rèn)情況下,Spring會(huì)對(duì)unchecked異常進(jìn)行事務(wù)回滾;如果是checked異常則不回滾。 辣么什么是checked異常,什么是unchecked異常
java里面將派生于Error或者RuntimeException(比如空指針,1/0)的異常稱為unchecked異常,其他繼承自java.lang.Exception得異常統(tǒng)稱為Checked Exception,如IOException、TimeoutException等
辣么再通俗一點(diǎn):你寫(xiě)代碼出現(xiàn)的空指針等異常,會(huì)被回滾,文件讀寫(xiě),網(wǎng)絡(luò)出問(wèn)題,spring就沒(méi)法回滾了。然后我教大家怎么記這個(gè),因?yàn)楹芏嗤瑢W(xué)容易弄混,你寫(xiě)代碼的時(shí)候有些IOException我們的編譯器是能夠檢測(cè)到的,說(shuō)以叫checked異常,你寫(xiě)代碼的時(shí)候空指針等死檢測(cè)不到的,所以叫unchecked異常。這樣是不是好記一些啦
4、只讀事務(wù):
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
只讀標(biāo)志只在事務(wù)啟動(dòng)時(shí)應(yīng)用,否則即使配置也會(huì)被忽略。
啟動(dòng)事務(wù)會(huì)增加線程開(kāi)銷,數(shù)據(jù)庫(kù)因共享讀取而鎖定(具體跟數(shù)據(jù)庫(kù)類型和事務(wù)隔離級(jí)別有關(guān))。通常情況下,僅是讀取數(shù)據(jù)時(shí),不必設(shè)置只讀事務(wù)而增加額外的系統(tǒng)開(kāi)銷。
二:事務(wù)傳播模式
Propagation枚舉了多種事務(wù)傳播模式,部分列舉如下:
1、REQUIRED(默認(rèn)模式):業(yè)務(wù)方法需要在一個(gè)容器里運(yùn)行。如果方法運(yùn)行時(shí),已經(jīng)處在一個(gè)事務(wù)中,那么加入到這個(gè)事務(wù),否則自己新建一個(gè)新的事務(wù)。
2、NOT_SUPPORTED:聲明方法不需要事務(wù)。如果方法沒(méi)有關(guān)聯(lián)到一個(gè)事務(wù),容器不會(huì)為他開(kāi)啟事務(wù),如果方法在一個(gè)事務(wù)中被調(diào)用,該事務(wù)會(huì)被掛起,調(diào)用結(jié)束后,原先的事務(wù)會(huì)恢復(fù)執(zhí)行。
3、REQUIRESNEW:不管是否存在事務(wù),該方法總匯為自己發(fā)起一個(gè)新的事務(wù)。如果方法已經(jīng)運(yùn)行在一個(gè)事務(wù)中,則原有事務(wù)掛起,新的事務(wù)被創(chuàng)建。
4、 MANDATORY:該方法只能在一個(gè)已經(jīng)存在的事務(wù)中執(zhí)行,業(yè)務(wù)方法不能發(fā)起自己的事務(wù)。如果在沒(méi)有事務(wù)的環(huán)境下被調(diào)用,容器拋出例外。
5、SUPPORTS:該方法在某個(gè)事務(wù)范圍內(nèi)被調(diào)用,則方法成為該事務(wù)的一部分。如果方法在該事務(wù)范圍外被調(diào)用,該方法就在沒(méi)有事務(wù)的環(huán)境下執(zhí)行。
6、NEVER:該方法絕對(duì)不能在事務(wù)范圍內(nèi)執(zhí)行。如果在就拋例外。只有該方法沒(méi)有關(guān)聯(lián)到任何事務(wù),才正常執(zhí)行。
7、NESTED:如果一個(gè)活動(dòng)的事務(wù)存在,則運(yùn)行在一個(gè)嵌套的事務(wù)中。如果沒(méi)有活動(dòng)事務(wù),則按REQUIRED屬性執(zhí)行。它使用了一個(gè)單獨(dú)的事務(wù),這個(gè)事務(wù)擁有多個(gè)可以回滾的保存點(diǎn)。內(nèi)部事務(wù)的回滾不會(huì)對(duì)外部事務(wù)造成影響。它只對(duì)DataSourceTransactionManager事務(wù)管理器起效。
三:解決Transactional注解不回滾
1、檢查你方法是不是public的
2、你的異常類型是不是unchecked異常 如果我想check異常也想回滾怎么辦,注解上面寫(xiě)明異常類型即可
@Transactional(rollbackFor=Exception.class)
類似的還有norollbackFor,自定義不回滾的異常
3、數(shù)據(jù)庫(kù)引擎要支持事務(wù),如果是MySQL,注意表要使用支持事務(wù)的引擎,比如innodb,如果是myisam,事務(wù)是不起作用的
4、是否開(kāi)啟了對(duì)注解的解析
5、spring是否掃描到你這個(gè)包,如下是掃描到org.test下面的包
6、檢查是不是同一個(gè)類中的方法調(diào)用(如a方法調(diào)用同一個(gè)類中的b方法)
7、異常是不是被你catch住了
問(wèn)題: 為什么方法修飾符不是public就會(huì)出現(xiàn)事務(wù)注解失效?
這幾天,同事遇到一個(gè)問(wèn)題,方向Aop 切入Controller,打請(qǐng)求日志,結(jié)果調(diào)Service層的方法報(bào)空指針錯(cuò)誤,由于是service沒(méi)有注入進(jìn)來(lái)。用了@Autowired和@Resource注解都注入不進(jìn)來(lái)。一行一行的檢查代碼,都沒(méi)有發(fā)現(xiàn)錯(cuò)誤,后來(lái)只能一個(gè)方法一個(gè)方法的刪除,看到哪里可以運(yùn)行,結(jié)果發(fā)現(xiàn)是因?yàn)镃ontroller方法是private私有類型的。后來(lái)改了成public就可以了。
貌似不能攔截私有方法?
試了很多次,都失敗了,是不是不行???
我想了一下,因?yàn)閍op底層是代理,
jdk是代理接口,私有方法必然不會(huì)存在在接口里,所以就不會(huì)被攔截到;
cglib是子類,private的方法照樣不會(huì)出現(xiàn)在子類里,也不能被攔截。
我不是類內(nèi)部直接調(diào)用方法,而是通過(guò)維護(hù)一個(gè)自身實(shí)例的代理
execution(* test.aop.ServiceA.*(..))
public class ServiceA { private ServiceA self; public void setSelf(ServiceA self) { this.self = self; } public String methodA(String str) { System.out.println("methodA: args=" + str); self.methodB("b"); return "12345" + str; } private String methodB(String str) { System.out.println("methodB: args=" + str); self.methodC("c"); return "12345" + str; } public String methodC(String str) { System.out.println("methodC: args=" + str); return "12345" + str; } }
是不是這么回事? 但是stackoverflow上,有人說(shuō) it works fine
execution(public * test.aop.ServiceA.*(..))
還有個(gè)奇怪的現(xiàn)象,execution里如果不寫(xiě)權(quán)限,那么public protected package的方法都能被攔截到
如果寫(xiě)了public,那就只攔截public方法這個(gè)沒(méi)問(wèn)題,
如果寫(xiě)了protected,他就什么事情都不做,連protected的方法也不攔截。
分析
private方法 在Spring使用純Spring AOP(只能攔截public/protected/包)都是無(wú)法被攔截的 因?yàn)樽宇悷o(wú)法覆蓋;包級(jí)別能被攔截的原因是,如果子類和父類在同一個(gè)包中是能覆蓋的。
在cglib代理情況下, execution(* *(..)) 可以攔截 public/protected/包級(jí)別方法(即這些方法都是能代理的)。
private static boolean isOverridable(Method method, Class targetClass) { if (Modifier.isPrivate(method.getModifiers())) { return false; } if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) { return true; } return getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass)); }
如果想要實(shí)現(xiàn)攔截private方法的 可以使用 原生 AspectJ 編譯期/運(yùn)行期織入。
引用
如果寫(xiě)了protected,他就什么事情都不做,連protected的方法也不攔截;這個(gè)應(yīng)該不會(huì)
原因基本分析明白了:
是否能應(yīng)用增強(qiáng)的判斷代碼如下(org.springframework.aop.support.AopUtils):
public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) { if (!pc.getClassFilter().matches(targetClass)) { return false; } MethodMatcher methodMatcher = pc.getMethodMatcher(); IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set classes = new HashSet(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); classes.add(targetClass); for (Iterator it = classes.iterator(); it.hasNext();) { Class clazz = (Class) it.next(); Method[] methods = clazz.getMethods(); for (int j = 0; j < methods.length; j++) { if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher.matches(methods[j], targetClass, hasIntroductions)) || methodMatcher.matches(methods[j], targetClass)) { return true; } } } return false; }
此處Method[] methods = clazz.getMethods();只能拿到public方法。。
場(chǎng)景1:execution(* *(..))
public class Impl2 { protected/public String testAop2() { System.out.println("234"); return "1233"; } }
因?yàn)榍腥朦c(diǎn)沒(méi)有訪問(wèn)修飾符,即可以是任意,因此canApply方法能拿到如wait這種public方法,即可以實(shí)施代理。
場(chǎng)景2:execution(public * *(..))
public class Impl2 { public String testAop2() { System.out.println("234"); return "1233"; } }
因?yàn)閿r截public的,因此canApply方法能拿到如wait這種public方法,即可以實(shí)施代理。
場(chǎng)景3:execution(protected * *(..))
public class Impl2 { protected String testAop2() { System.out.println("234"); return "1233"; } }
還記得之前說(shuō)過(guò),在canApply方法中 的 Method[] methods = clazz.getMethods();只能拿到public方法的,因此跟protected訪問(wèn)修飾符是無(wú)法匹配的,所以如果“execution(protected * *(..))” 是 無(wú)法代理的。
這就是為什么execution(protected * *(..))在純Spring AOP環(huán)境下不行的原因。
注,@Transactional注解事務(wù)的特殊情況:
引用
方法的可見(jiàn)度和 @Transactional
在使用代理的時(shí)候,@Transactional 注解應(yīng)該只被應(yīng)用到 public 可見(jiàn)度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,系統(tǒng)也不會(huì)報(bào)錯(cuò), 但是這個(gè)被注解的方法將不會(huì)執(zhí)行已配置的事務(wù)設(shè)置。
以上是“Spring中@Transactional注解失效怎么辦”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!