Spring@Transactional注解失效解決方案

這篇文章主要介紹了Spring @Transactional注解失效解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

在豐順等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作 網(wǎng)站設(shè)計(jì)制作按需定制開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),網(wǎng)絡(luò)營(yíng)銷推廣,外貿(mào)網(wǎng)站制作,豐順網(wǎng)站建設(shè)費(fèi)用合理。

這幾天在項(xiàng)目里面發(fā)現(xiàn)我使用@Transactional注解事務(wù)之后,拋了異常居然不回滾。后來終于找到了原因。
如果你也出現(xiàn)了這種情況,可以從下面開始排查。

一、特性

先來了解一下@Transactional注解事務(wù)的特性吧,可以更好排查問題

1、service類標(biāo)簽(一般不建議在接口上)上添加@Transactional,可以將整個(gè)類納入spring事務(wù)管理,在每個(gè)業(yè)務(wù)方法執(zhí)行時(shí)都會(huì)開啟一個(gè)事務(wù),不過這些事務(wù)采用相同的管理方式。

2、@Transactional 注解只能應(yīng)用到 public 可見度的方法上。 如果應(yīng)用在protected、private或者 package可見度的方法上,也不會(huì)報(bào)錯(cuò),不過事務(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àn)的空指針等異常,會(huì)被回滾,文件讀寫,網(wǎng)絡(luò)出問題,spring就沒法回滾了。然后我教大家怎么記這個(gè),因?yàn)楹芏嗤瑢W(xué)容易弄混,你寫代碼的時(shí)候有些IOException我們的編譯器是能夠檢測(cè)到的,說以叫checked異常,你寫代碼的時(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ì)增加線程開銷,數(shù)據(jù)庫(kù)因共享讀取而鎖定(具體跟數(shù)據(jù)庫(kù)類型和事務(wù)隔離級(jí)別有關(guān))。通常情況下,僅是讀取數(shù)據(jù)時(shí),不必設(shè)置只讀事務(wù)而增加額外的系統(tǒng)開銷。

二:事務(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ù)。如果方法沒有關(guān)聯(lián)到一個(gè)事務(wù),容器不會(huì)為他開啟事務(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ù)。如果在沒有事務(wù)的環(huán)境下被調(diào)用,容器拋出例外。

5、SUPPORTS:該方法在某個(gè)事務(wù)范圍內(nèi)被調(diào)用,則方法成為該事務(wù)的一部分。如果方法在該事務(wù)范圍外被調(diào)用,該方法就在沒有事務(wù)的環(huán)境下執(zhí)行。

6、NEVER:該方法絕對(duì)不能在事務(wù)范圍內(nèi)執(zhí)行。如果在就拋例外。只有該方法沒有關(guān)聯(lián)到任何事務(wù),才正常執(zhí)行。

7、NESTED:如果一個(gè)活動(dòng)的事務(wù)存在,則運(yùn)行在一個(gè)嵌套的事務(wù)中。如果沒有活動(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異常也想回滾怎么辦,注解上面寫明異常類型即可

@Transactional(rollbackFor=Exception.class) 

類似的還有norollbackFor,自定義不回滾的異常

3、數(shù)據(jù)庫(kù)引擎要支持事務(wù),如果是MySQL,注意表要使用支持事務(wù)的引擎,比如innodb,如果是myisam,事務(wù)是不起作用的

4、是否開啟了對(duì)注解的解析

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

5、spring是否掃描到你這個(gè)包,如下是掃描到org.test下面的包

<context:component-scan base-package="org.test" ></context:component-scan>

6、檢查是不是同一個(gè)類中的方法調(diào)用(如a方法調(diào)用同一個(gè)類中的b方法)

7、異常是不是被你catch住了

問題: 為什么方法修飾符不是public就會(huì)出現(xiàn)事務(wù)注解失效?

這幾天,同事遇到一個(gè)問題,方向Aop 切入Controller,打請(qǐng)求日志,結(jié)果調(diào)Service層的方法報(bào)空指針錯(cuò)誤,由于是service沒有注入進(jìn)來。用了@Autowired和@Resource注解都注入不進(jìn)來。一行一行的檢查代碼,都沒有發(fā)現(xiàn)錯(cuò)誤,后來只能一個(gè)方法一個(gè)方法的刪除,看到哪里可以運(yùn)行,結(jié)果發(fā)現(xiàn)是因?yàn)镃ontroller方法是private私有類型的。后來改了成public就可以了。

貌似不能攔截私有方法?

試了很多次,都失敗了,是不是不行啊?

我想了一下,因?yàn)閍op底層是代理,

jdk是代理接口,私有方法必然不會(huì)存在在接口里,所以就不會(huì)被攔截到;

cglib是子類,private的方法照樣不會(huì)出現(xiàn)在子類里,也不能被攔截。

我不是類內(nèi)部直接調(diào)用方法,而是通過維護(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上,有人說 it works fine

execution(public * test.aop.ServiceA.*(..))

還有個(gè)奇怪的現(xiàn)象,execution里如果不寫權(quán)限,那么public protected package的方法都能被攔截到

如果寫了public,那就只攔截public方法這個(gè)沒問題,

如果寫了protected,他就什么事情都不做,連protected的方法也不攔截。

分析

private方法 在Spring使用純Spring AOP(只能攔截public/protected/包)都是無法被攔截的 因?yàn)樽宇悷o法覆蓋;包級(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)行期織入。

引用

如果寫了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)沒有訪問修飾符,即可以是任意,因此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"; 
  } 
}

還記得之前說過,在canApply方法中 的 Method[] methods = clazz.getMethods();只能拿到public方法的,因此跟protected訪問修飾符是無法匹配的,所以如果“execution(protected * *(..))” 是 無法代理的。

這就是為什么execution(protected * *(..))在純Spring AOP環(huán)境下不行的原因。

注,@Transactional注解事務(wù)的特殊情況:

引用

方法的可見度和 @Transactional

在使用代理的時(shí)候,@Transactional 注解應(yīng)該只被應(yīng)用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,系統(tǒng)也不會(huì)報(bào)錯(cuò), 但是這個(gè)被注解的方法將不會(huì)執(zhí)行已配置的事務(wù)設(shè)置。如果你非要注解非公共方法的話,請(qǐng)參考使用AspectJ

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

新聞名稱:Spring@Transactional注解失效解決方案
本文地址:http://bm7419.com/article38/pcodpp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、網(wǎng)站維護(hù)、用戶體驗(yàn)、Google、自適應(yīng)網(wǎng)站、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)