自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring事務(wù)失效的各種場(chǎng)景總結(jié)及源碼分析

開(kāi)發(fā) 架構(gòu)
在本文中,深入探討了Spring事務(wù)失效的各種情況。通過(guò)了解這些情況,我們可以更好地理解事務(wù)管理在Spring框架中的重要性,以及如何避免和解決事務(wù)失效的問(wèn)題。

環(huán)境:Spring5.3.23

1. 簡(jiǎn)介

在Spring框架中,事務(wù)管理是保障數(shù)據(jù)一致性和系統(tǒng)可靠性的重要手段。但在實(shí)際開(kāi)發(fā)中,Spring事務(wù)失效的問(wèn)題卻時(shí)有發(fā)生。本文將總結(jié)并分析Spring事務(wù)失效的各種場(chǎng)景,幫助你全面了解事務(wù)失效的原因和解決方案,讓你不再被事務(wù)問(wèn)題困擾。。讓我們一起揭開(kāi)Spring事務(wù)失效的神秘面紗,迎接更穩(wěn)健、高效的系統(tǒng)開(kāi)發(fā)之旅!

2. 事務(wù)失效場(chǎng)景

2.1 非public方法

@Transactional
protected void save() {
  Person person = new Person();
  person.setAge(36);
  person.setName("張三");
  int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(),
      person.getName());
  System.out.println("save Db Update " + result + " 次");
  System.out.println(1 / 0) ;
}

以上方法是protected修飾的,事務(wù)將失效,默認(rèn)Spring支持支public修飾的方法。如何讓Spring支持非public方法呢?可以通過(guò)如下方法修改

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
  // 設(shè)置為false,這樣protected及默認(rèn)修飾的方法都將支持事務(wù)功能
  return new AnnotationTransactionAttributeSource(false) ;
}

該要想上面bean生效,你還需要開(kāi)啟如下功能

GenericApplicationContext context = new GenericApplicationContext();
// 允許Bean覆蓋,后面的BeanDefintion能覆蓋前面的
// 我們定義的transactionAttributeSource bena能夠覆蓋系統(tǒng)默認(rèn)的
context.setAllowBeanDefinitionOverriding(true) ;

2.2 異常被吞

@Transactional
protected void save() {
  try {
    // ...
    int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(),
        person.getName());
    System.out.println(1 / 0) ;
  } catch (Exception e) {
    e.printStackTrace() ;
  }
}

上面代碼將異常信息捕獲了后并沒(méi)有再進(jìn)行拋出。Spring 事務(wù)的原理就是根據(jù)你代碼執(zhí)行時(shí)是否發(fā)生了異常來(lái)控制事務(wù)是否回滾。源碼如下:

Spring事務(wù)的核心攔截器TransactionInterceptor

public abstract class TransactionAspectSupport {
  protected Object invokeWithinTransaction(...) throws Throwable {
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
    Object retVal;
      try {
        // 執(zhí)行實(shí)際的業(yè)務(wù)代碼調(diào)用
        retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
        // 執(zhí)行事務(wù)回滾
        completeTransactionAfterThrowing(txInfo, ex);
        // 繼續(xù)拋出,終止向下執(zhí)行
        throw ex;
      }
      finally {
        cleanupTransactionInfo(txInfo);
      }
      // 沒(méi)有異常則進(jìn)行事務(wù)的提交
      commitTransactionAfterReturning(txInfo);
  }
}

2.3 回滾異常類設(shè)置錯(cuò)誤

Spring事務(wù)回滾策略是只會(huì)回滾RuntimeException與Error類型的異常和錯(cuò)誤。

@Transactional
protected void save() throws Exception {
  try {
    Person person = new Person();
    person.setAge(36);
    person.setName("張三");
    int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(),
        person.getName());
    System.out.println("save Db Update " + result + " 次");
    System.out.println(1 / 0) ;
  } catch (Exception e) {
    e.printStackTrace() ;
    throw new Exception(e) ;
  }
}

這里并沒(méi)有設(shè)置rollbackFor屬性,所以這里事務(wù)不會(huì)被回滾?;貪L邏輯處理如下:

public abstract class TransactionAspectSupport {
  protected Object invokeWithinTransaction() {
    try {
      retVal = invocation.proceedWithInvocation();
    }
    catch (Throwable ex) {
      // 回滾處理
      completeTransactionAfterThrowing(txInfo, ex);
      throw ex;
    }
  }
  protected void completeTransactionAfterThrowing() {
    // 檢查異常
    if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
      try {
        txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
      }
    }
  }
}
public abstract class DelegatingTransactionAttribute {
  // 實(shí)現(xiàn)類是下面的RuleBasedTransactionAttribute
  private final TransactionAttribute targetAttribute;
  public boolean rollbackOn(Throwable ex) {
    return this.targetAttribute.rollbackOn(ex);
  }
}
public class RuleBasedTransactionAttribute {
  public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null;
    int deepest = Integer.MAX_VALUE;


    // 遍歷處理你配置的rollbackFor屬性配置
    if (this.rollbackRules != null) {
      for (RollbackRuleAttribute rule : this.rollbackRules) {
        int depth = rule.getDepth(ex);
        if (depth >= 0 && depth < deepest) {
          deepest = depth;
          winner = rule;
        }
      }
    }
    
    // 如果上沒(méi)有找到異常,則進(jìn)行默認(rèn)行為的處理,檢查異常類型
    if (winner == null) {
      return super.rollbackOn(ex);
    }


    return !(winner instanceof NoRollbackRuleAttribute);
  }
  public boolean rollbackOn(Throwable ex) {
    // 回滾是運(yùn)行時(shí)及Error類型的異?;蝈e(cuò)誤
    return (ex instanceof RuntimeException || ex instanceof Error);
  }
}

2.4 同一類中方法互相調(diào)用

protected void save() {
  // ...
  this.updatePerson()
}
@Transactional
public void updatePerson() {
  // ...
}

上面的事務(wù)將會(huì)失效,因?yàn)樵趕ave中通過(guò)this調(diào)用updatePerson,而這時(shí)的this是原始對(duì)象,并不是當(dāng)前容器中生成的那個(gè)代理對(duì)象,通過(guò)如下方式解決:

方式1:

protected void save() {
  // 通過(guò)AopContext獲取當(dāng)前代理對(duì)象
  PersonService proxy = (PersonService)AopContext.currentProxy() ;
  proxy.save() ;
}

這種方式,不推薦;這將你的代碼與Spring AOP完全耦合,并使類本身意識(shí)到它正在AOP上下文中使用,這與AOP背道而馳。

方式2:

自己注入自己

@Resource
private PersonService personService ;
public void save() {
  personService.save() ;
}

2.5 方法被final修飾

@Transactional
protected final void save() {
  // ...
}

方法被final修飾,cglib是通過(guò)繼承的方式實(shí)現(xiàn)代理,final修飾后將不能重寫(xiě)save方法。程序拋出NPE異常

Exception in thread "main" java.lang.NullPointerException
  at com.pack.main.transaction.TransactionNoPublicMethodMain2$PersonService.save(TransactionNoPublicMethodMain2.java:98)

因?yàn)闊o(wú)法重寫(xiě)save方法,首先是沒(méi)法對(duì)方法進(jìn)行增強(qiáng)處理,其次只能調(diào)用父類的save方法,而父類中的所有屬性(需要注入的)都將是null。

2.6 傳播類型設(shè)置錯(cuò)誤

@Transactional(propagation = Propagation.NOT_SUPPORTED)
protected void save() {
  // ...
}

或者是設(shè)置為Propagation.NEVER,這都將使得事務(wù)失效。部分源碼:

public abstract class TransactionAspectSupport {
  protected Object invokeWithinTransaction() {
    // 使用getTransaction和commit/rollback調(diào)用進(jìn)行標(biāo)準(zhǔn)事務(wù)劃分。
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
  }
  protected TransactionInfo createTransactionIfNecessary() {
    // 調(diào)用事務(wù)管理器獲取事務(wù)對(duì)象
    status = tm.getTransaction(txAttr);
  }
}
public abstract class AbstractPlatformTransactionManager {
  public final TransactionStatus getTransaction() {
    // 根據(jù)配置的事務(wù)傳播屬性進(jìn)行相應(yīng)的處理
    if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
      throw new IllegalTransactionStateException(
          "No existing transaction found for transaction marked with propagation 'mandatory'");
    }
    else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
        def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
        def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    } else {
      // 創(chuàng)建“空”事務(wù):沒(méi)有實(shí)際的事務(wù),但可能是同步。
      boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
      return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }
  }
}

2.7 異步線程執(zhí)行

在一個(gè)事務(wù)方法中開(kāi)啟新的線程執(zhí)行事務(wù)方法

@Transactional()
protected void save() {
  new Thread(() -> {
    Person person = new Person();
    person.setAge(36);
    person.setName("張三");
    int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(),
        person.getName());
    System.out.println("save Db Update " + result + " 次");
    System.out.println(1 / 0) ;
  }).start() ;
  try {
    TimeUnit.SECONDS.sleep(3) ;
  } catch (InterruptedException e) {}
}

上面的事務(wù)將不會(huì)生效,這是因?yàn)橹骶€程與子線程使用的不是同一個(gè)Connection對(duì)象,Spring事務(wù)執(zhí)行會(huì)為每一個(gè)執(zhí)行線程綁定一個(gè)Connection對(duì)象。源碼如下:

public abstract class AbstractPlatformTransactionManager {
  // 開(kāi)始新的事務(wù)
  private TransactionStatus startTransaction() {
    doBegin(transaction, definition);
  }
}
public class DataSourceTransactionManager {
  protected void doBegin(...) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;
    try {
      if (!txObject.hasConnectionHolder() ||
          txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
        // 獲取連接對(duì)象
        Connection newCon = obtainDataSource().getConnection();
        txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
      }
      // 將連接對(duì)象綁定到當(dāng)前線程上
      if (txObject.isNewConnectionHolder()) {
        TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
      }
    } 
  }
}

你新啟動(dòng)的線程是拿不到主線程中的Connection。

2.8 數(shù)據(jù)庫(kù)不支持

在MySQL建表時(shí)指定了錯(cuò)誤的引擎,比如使用了MyISAM。mysql支持哪些引擎及事務(wù)支持情況如下:

支持事務(wù)的只有InnoDB。在建表時(shí)明確指定引擎。

通過(guò)上面的方式制定ENGINE=InnoDB。

2.9 關(guān)于@Transactional注解使用錯(cuò)誤的情況

有些人說(shuō)使用了錯(cuò)誤的@javax.transaction.Transactional注解。通過(guò)源碼分析

Spring在定義事務(wù)的切面時(shí),會(huì)使用TransactionAttributeSource來(lái)判斷當(dāng)前的類上或者是方法上是否有@Transactional注解

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
  return new AnnotationTransactionAttributeSource();
}
public class AnnotationTransactionAttributeSource {
  private static final boolean jta12Present;
  private static final boolean ejb3Present;
  static {
    // 判斷是否存在該注解類
    jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
  }
  public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
    this.publicMethodsOnly = publicMethodsOnly;
    if (jta12Present || ejb3Present) {
      this.annotationParsers = new LinkedHashSet<>(4);
      this.annotationParsers.add(new SpringTransactionAnnotationParser());
      if (jta12Present) {
        // 如果存在會(huì)加入專門解析@javax.transaction.Transactional注解的解析器類
        this.annotationParsers.add(new JtaTransactionAnnotationParser());
      }
      if (ejb3Present) {
        this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
      }
    }
    else {
      this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    }
  }
}

所以如果你類路徑下只要存在,那么你的事務(wù)還是可以生效的。

總結(jié):在本文中,深入探討了Spring事務(wù)失效的各種情況。通過(guò)了解這些情況,我們可以更好地理解事務(wù)管理在Spring框架中的重要性,以及如何避免和解決事務(wù)失效的問(wèn)題。

完畢!??!

責(zé)任編輯:武曉燕 來(lái)源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2022-02-14 16:53:57

Spring項(xiàng)目數(shù)據(jù)庫(kù)

2023-07-05 08:45:18

Spring事務(wù)失效場(chǎng)景

2024-09-09 08:29:25

2024-01-05 08:38:20

SpringBeanScope

2021-09-04 07:56:44

Spring事務(wù)失效

2021-12-13 11:12:41

Spring事務(wù)失效

2021-04-14 15:17:08

Transaction代碼語(yǔ)言

2025-02-10 00:27:54

2023-09-28 09:07:54

注解失效場(chǎng)景

2022-04-13 20:53:15

Spring事務(wù)管理

2022-12-06 10:39:43

Spring事務(wù)失效

2022-09-22 09:57:20

Spring事務(wù)失效

2021-04-28 06:26:11

Spring Secu功能實(shí)現(xiàn)源碼分析

2022-07-05 14:19:30

Spring接口CGLIB

2023-05-26 07:19:49

Spring聲明式事務(wù)

2023-08-29 10:51:44

2023-09-08 08:52:12

Spring注解事務(wù)

2023-04-28 08:21:36

SpringBoot聲明式事務(wù)編程式事務(wù)

2020-08-19 09:45:29

Spring數(shù)據(jù)庫(kù)代碼

2024-11-13 19:03:14

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)