最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中this調(diào)用會(huì)導(dǎo)致事務(wù)失效的深入分析

 更新時(shí)間:2026年04月13日 08:48:55   作者:提前退休的java猿  
在Java中this是一個(gè)非常重要的關(guān)鍵字,它表示當(dāng)前對(duì)象的引用,也就是說(shuō),當(dāng)你在某個(gè)類(lèi)的實(shí)例方法或構(gòu)造器中時(shí),this指向調(diào)用該方法或創(chuàng)建的當(dāng)前對(duì)象實(shí)例,這篇文章主要介紹了Java中this調(diào)用會(huì)導(dǎo)致事務(wù)失效的相關(guān)資料,需要的朋友可以參考下

前言

前一兩周,公司的業(yè)務(wù)出了重大問(wèn)題。數(shù)據(jù)庫(kù)的服務(wù)器CPU 100%然后一些重要的抽獎(jiǎng)業(yè)務(wù)竟然出現(xiàn)物品超領(lǐng)的情況。

同事的反饋就是 一個(gè)事務(wù)里面 只執(zhí)行了后半部分,也就是領(lǐng)取記錄插入(B操作)成功了,庫(kù)存扣減(A操作)是沒(méi)有問(wèn)題的。

核心代碼如下(大家看一下事務(wù)是否失效??):

//----------------------------------------controller代碼---------------------------------------
@RedisRateLimiter(value = 100,limit = 1)
@PostMapping(value = "/grabCoupon")
public Res<?> equityClaim(@RequestBody GrabCouponReqEx req) {
    .........service 使用@Resource 注入..... 
    return service.grabCouponTrans(req);
}
---------------------------------------service impl代碼----------------------------------------
@Transactional(rollbackFor = RuntimeException.class)
@Override
public Result<?> grabCouponTrans(GrabCouponReqEx req) {
       // 對(duì)所有代碼進(jìn)行 try,然后拋出RuntimException 讓框架回滾
       try{
       .............................
        //? A操作:庫(kù)存扣減,這個(gè)地方對(duì)庫(kù)存-1,如果更新行數(shù)大與0返回true 
        //UPDATE t SET num =  num -1  WHERE id = #{id} AND num > 0;
        boolean isok = reduceInventory(id);
        if (isok) {
            // ?B操作:插入領(lǐng)取記錄,插入成功返回 true
            Boolean insetSuc = insertRecord(record);
            if (!insetSuc) {
               throw new RuntimeException("領(lǐng)取失?。?);
            }
            return result;
        } 
    }catch (Exception e){
        throw new RuntimeException("xxxx");
    }
}

??:很多網(wǎng)友都說(shuō)this調(diào)用了reduceInventory、insertRecord方法事物不會(huì)生效

?這種理解肯定是不對(duì)的,this調(diào)用的外層方法是加了事物注解,并且是@Resource注入調(diào)用,是能被代理的。外層方法都被攔截了代理了,方法內(nèi)部再this調(diào)用也是沒(méi)有問(wèn)題的,默認(rèn)就加入當(dāng)前事務(wù)了,如果內(nèi)部方法通過(guò)容器對(duì)象獲取,并且有事物注解這時(shí)候就是看事物的傳播機(jī)制了。

事務(wù)源碼分析

下面就是事務(wù)攔截器主要的代碼邏輯 TransactionAspectSupport.invokeWithinTransaction:

  • 創(chuàng)建或獲取事務(wù)
  • 執(zhí)行業(yè)務(wù)方法
  • 捕捉異常:有異常處理回滾、沒(méi)有匹配到回滾異常則提交事務(wù)
  • 清理事務(wù)信息
  • 提交事務(wù)
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
       final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final TransactionManager tm = determineTransactionManager(txAttr);
    // 處理響應(yīng)式事務(wù)(Spring 5.2+)
    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
       ............................
       return result;
    }
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
       // ?關(guān)鍵步驟1:創(chuàng)建或獲取事務(wù)(創(chuàng)建新事務(wù)還是沿用使用就看傳播機(jī)制了)
       // Standard transaction demarcation with getTransaction and commit/rollback calls.
       TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
       Object retVal;
       try {
          // ?關(guān)鍵步驟2:執(zhí)行業(yè)務(wù)方法(AOP鏈的下一個(gè)攔截器或目標(biāo)方法) 
          (執(zhí)行目標(biāo)方法:內(nèi)部調(diào)用的方法使用this指向默認(rèn)也是被攔截的,
          如果有事務(wù)注解并且通過(guò)容器對(duì)象調(diào)用,執(zhí)行的時(shí)候就會(huì)再次被攔截,此時(shí)事務(wù)的傳播機(jī)制的作用就體現(xiàn)出來(lái)了)
          // This is an around advice: Invoke the next interceptor in the chain.
          // This will normally result in a target object being invoked.
          retVal = invocation.proceedWithInvocation();
       }
       catch (Throwable ex) {
          // ?關(guān)鍵步驟3:異常處理回滾
          // target invocation exception
          completeTransactionAfterThrowing(txInfo, ex);
          throw ex;
       }
       finally {
          //?關(guān)鍵步驟4:清理事務(wù)信息
          cleanupTransactionInfo(txInfo);
       }
       if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
          // Set rollback-only in case of Vavr failure matching our rollback rules...
          TransactionStatus status = txInfo.getTransactionStatus();
          if (status != null && txAttr != null) {
             retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
          }
       }
       // ?關(guān)鍵步驟5:提交事務(wù)
       commitTransactionAfterReturning(txInfo);
       return retVal;
    }
    else {
      //**JTA(Java Transaction API)**  或需要回調(diào)機(jī)制的事務(wù)管理器
      //...........................................................
    }
}

一、創(chuàng)建獲取事務(wù)

創(chuàng)建事務(wù)信息createTransactionIfNecessary,如果之前已經(jīng)存在事務(wù)就走需要判斷事務(wù)的傳播機(jī)制了

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
        @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    // 如果沒(méi)有指定名稱(chēng),使用方法標(biāo)識(shí)作為事務(wù)名稱(chēng)
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }
    TransactionStatus status = null;
    if (txAttr != null) {
        // ?關(guān)鍵:根據(jù)傳播行為獲取事務(wù)狀態(tài)
        if (tm != null) {
            status = tm.getTransaction(txAttr);  // 這里處理PROPAGATION_REQUIRED等
        }
        // ...
    }
    // ?準(zhǔn)備事務(wù)信息
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

這是核心中的核心,在 AbstractPlatformTransactionManager.getTransaction() 中:

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    // 1. 獲取事務(wù)定義
    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
    // 2. 獲取現(xiàn)有事務(wù)(處理傳播行為)
    Object transaction = doGetTransaction();
    // 3. 檢查當(dāng)前是否存在事務(wù)
    if (isExistingTransaction(transaction)) {
        // 已存在事務(wù):根據(jù)傳播行為處理
        return handleExistingTransaction(def, transaction, debugEnabled);
    }
    // 4. 沒(méi)有現(xiàn)有事務(wù):檢查超時(shí)等設(shè)置
    if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
    }
    // 5. 需要新事務(wù):PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW, PROPAGATION_NESTED
    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) {
        // 掛起現(xiàn)有資源(如果有)
        SuspendedResourcesHolder suspendedResources = suspend(null);
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            DefaultTransactionStatus status = newTransactionStatus(
                def, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            // 關(guān)鍵:開(kāi)始新事務(wù)
            doBegin(transaction, def);
            prepareSynchronization(status, def);
            return status;
        }
        catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    else {
        // 6. 空事務(wù):PROPAGATION_SUPPORTS, PROPAGATION_NOT_SUPPORTED, PROPAGATION_NEVER
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }
}

二、異常回滾

執(zhí)行回滾 : 匹配指定設(shè)置的異常,匹配不到就匹配 RuntimeException 或 Error

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        // 關(guān)鍵:根據(jù)回滾規(guī)則決定是否回滾
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                // 執(zhí)行回滾 : 匹配指定設(shè)置的異常,匹配不到就匹配 RuntimeException 或 Error
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {
                // 記錄錯(cuò)誤但不拋出
                logger.error("Application exception overridden by rollback exception", ex);
                ex2.initApplicationException(ex);
                throw ex2;
            }
            catch (RuntimeException | Error ex2) {
                logger.error("Application exception overridden by rollback exception", ex);
                throw ex2;
            }
        }
        else {
            // 不滿足回滾條件,提交事務(wù)
            try {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {
                // ... 異常處理
            }
        }
    }
}

三、為什么清理事務(wù)信息后才提交事務(wù)

在標(biāo)準(zhǔn)事務(wù)分支執(zhí)行邏輯如下,沒(méi)有發(fā)生異常的情況就是先清理事務(wù)信息然后提交事務(wù):

try {
    retVal = invocation.proceedWithInvocation();  // 執(zhí)行業(yè)務(wù)方法
}
catch (Throwable ex) {
    completeTransactionAfterThrowing(txInfo, ex);  // 異?;貪L
    throw ex;
}
finally {
    cleanupTransactionInfo(txInfo);  // 清理事務(wù)信息
}
commitTransactionAfterReturning(txInfo);  // 提交事務(wù)

1.事務(wù)信息與事務(wù)狀態(tài)分離

首先理解兩個(gè)關(guān)鍵概念:

  • TransactionInfo:線程綁定的事務(wù)上下文信息
  • TransactionStatus:實(shí)際的事務(wù)狀態(tài)(包含數(shù)據(jù)庫(kù)連接、回滾標(biāo)記等)
public class TransactionInfo {
    private final PlatformTransactionManager transactionManager;
    private final TransactionAttribute transactionAttribute;
    private final String joinpointIdentification;
    private TransactionStatus transactionStatus;  // 實(shí)際事務(wù)狀態(tài)
    private TransactionInfo oldTransactionInfo;   // 舊的事務(wù)信息(用于嵌套事務(wù))
}

關(guān)鍵點(diǎn):提交事務(wù)需要的是 TransactionStatus,而不是 TransactionInfo。清理的是線程綁定的上下文信息,而不是實(shí)際的事務(wù)狀態(tài)。

2. cleanupTransactionInfo 實(shí)際做了什么?

protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
    if (txInfo != null) {
        // 恢復(fù)線程的事務(wù)狀態(tài)到之前的狀態(tài)
        txInfo.restoreThreadLocalStatus();
    }
}
// TransactionInfo.restoreThreadLocalStatus()
public void restoreThreadLocalStatus() {
    // 將線程綁定恢復(fù)到舊的事務(wù)信息(如果有的話)
    TransactionSynchronizationManager.bindResource(
        this.transactionManager, this.oldTransactionInfo);
}

重點(diǎn)cleanupTransactionInfo 只是恢復(fù)線程的 ThreadLocal 狀態(tài),并不影響實(shí)際事務(wù)的連接和狀態(tài)

3. 詳細(xì)執(zhí)行時(shí)序分析

可以看一下面事務(wù)嵌套的案例

@Service
public class UserService {
    @Transactional
    public void methodA() {
        // 事務(wù)A開(kāi)始
        userRepository.updateA();
        // 調(diào)用 methodB(REQUIRES_NEW)
        userServiceProxy.methodB();  // 通過(guò)代理調(diào)用
        // 事務(wù)A繼續(xù)
        userRepository.updateA2();
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 事務(wù)B開(kāi)始(掛起事務(wù)A)
        logRepository.save();
        // 事務(wù)B提交
    }
}

執(zhí)行時(shí)序: 大概的邏輯就是,執(zhí)行完一個(gè)事務(wù)就需要立即把當(dāng)前事務(wù)方法的外層方法的事務(wù)給恢復(fù)(存在的話)。

時(shí)間軸:
│
├─ 進(jìn)入 methodA()
│   ├─ 創(chuàng)建 TransactionInfo_A(綁定到 ThreadLocal)
│   ├─ 開(kāi)始事務(wù)A
│   │
│   ├─ 調(diào)用 methodB()(通過(guò)代理)
│   │   ├─ 創(chuàng)建 TransactionInfo_B(綁定到 ThreadLocal,保存舊的 TransactionInfo_A)
│   │   ├─ 掛起事務(wù)A
│   │   ├─ 開(kāi)始事務(wù)B
│   │   ├─ 執(zhí)行業(yè)務(wù)邏輯
│   │   │
│   │   ├─ 清理階段:
│   │   │   ├─ cleanupTransactionInfo(TransactionInfo_B) 
│   │   │   │   └─ 恢復(fù) ThreadLocal 到 TransactionInfo_A ?
│   │   │   │
│   │   │   ├─ 提交事務(wù)B ?
│   │   │   │   └─ 需要事務(wù)B的 TransactionStatus,但不需要 ThreadLocal 的 TransactionInfo_B
│   │   │   │
│   │   │   └─ 恢復(fù)事務(wù)A
│   │   └─ 返回
│   │
│   └─ methodA 繼續(xù)執(zhí)行
│       ├─ 使用 TransactionInfo_A(已恢復(fù))
│       ├─ 提交事務(wù)A
│       └─ 清理 TransactionInfo_A
└─ 結(jié)束

4. 提交事務(wù)不需要 ThreadLocal 綁定

// AbstractPlatformTransactionManager.commit()
public final void commit(TransactionStatus status) throws TransactionException {
    // 直接從 TransactionStatus 獲取實(shí)際事務(wù)對(duì)象
    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    // 檢查是否需要回滾
    if (defStatus.isLocalRollbackOnly() || defStatus.isGlobalRollbackOnly()) {
        processRollback(defStatus, false);
        return;
    }
    // 實(shí)際提交
    processCommit(defStatus);
}
// DataSourceTransactionManager.doCommit()
protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    // 直接使用連接提交
    con.commit();  // 不需要 ThreadLocal 信息
}

關(guān)鍵:提交操作只需要:

  1. 數(shù)據(jù)庫(kù)連接(從 TransactionStatus 獲?。?/li>
  2. 事務(wù)狀態(tài)(是否標(biāo)記為回滾)

?明白事務(wù)傳播機(jī)制以及事務(wù)的相關(guān)信息是綁定到ThreadLocal中之后,用異步的時(shí)候就要注意事務(wù)失效的問(wèn)題了

附:快速驗(yàn)證事務(wù)是否生效的方法

你可以在單元測(cè)試中這樣驗(yàn)證(概念代碼):

@SpringBootTest
class UserServiceTest {
    @Autowired
    private UserService userService;
    @Test
    void testTransactionWithThisCall() {
        // 預(yù)期:內(nèi)部 this 調(diào)用時(shí),異常不會(huì)回滾
        assertThrows(RuntimeException.class, () -> userService.addUserAndLog(new User()));
        // 檢查數(shù)據(jù)庫(kù),記錄應(yīng)該被插入(事務(wù)未回滾)
    }
    @Test
    void testTransactionViaProxy() {
        // 使用注入的代理對(duì)象直接調(diào)用事務(wù)方法,應(yīng)該回滾
        assertThrows(RuntimeException.class, () -> userService.insertUser(new User()));
        // 檢查數(shù)據(jù)庫(kù),記錄不應(yīng)該存在
    }
}

更精確的驗(yàn)證可以開(kāi)啟 SQL 日志,觀察是否打印 BEGIN 和 ROLLBACK。

總結(jié) 

到此這篇關(guān)于Java中this調(diào)用會(huì)導(dǎo)致事務(wù)失效的文章就介紹到這了,更多相關(guān)Java this調(diào)用事務(wù)失效內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 利用java獲取某個(gè)文件夾下的所有文件

    利用java獲取某個(gè)文件夾下的所有文件

    這篇文章主要給大家介紹了關(guān)于如何利用java獲取某個(gè)文件夾下的所有文件的相關(guān)資料,在從事web開(kāi)發(fā)工作中,經(jīng)常需要對(duì)本地某一個(gè)目錄下的文件進(jìn)行處理,需要的朋友可以參考下
    2023-07-07
  • 通過(guò)Docker啟動(dòng)Solace并在Spring?Boot通過(guò)JMS整合Solace的操作方法

    通過(guò)Docker啟動(dòng)Solace并在Spring?Boot通過(guò)JMS整合Solace的操作方法

    本文將介紹如何在Spring中使用,雖然代碼使用的是Spring Boot,但并沒(méi)有使用相關(guān)starter,跟Spring的整合一樣,可通用,JMS是通過(guò)的消息處理框架,可以深入學(xué)習(xí)一下,不同的MQ在JMS的整合上都是類(lèi)似的,感興趣的朋友跟隨小編一起看看吧
    2023-01-01
  • SpringBoot 集成 Nebula的操作過(guò)程

    SpringBoot 集成 Nebula的操作過(guò)程

    這篇文章主要介紹了SpringBoot 集成 Nebula的操作過(guò)程,通過(guò)示例代碼介紹了java 環(huán)境下如何對(duì) Nebula Graph 進(jìn)行操作,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • 使用Java調(diào)用??低昐DK實(shí)現(xiàn)攝像頭預(yù)覽超詳細(xì)教程

    使用Java調(diào)用??低昐DK實(shí)現(xiàn)攝像頭預(yù)覽超詳細(xì)教程

    現(xiàn)在制造業(yè)很多都是用的海康的攝像頭,作為程序員有時(shí)候需要對(duì)接??禂z像頭,這篇文章主要介紹了使用Java調(diào)用海康威視SDK實(shí)現(xiàn)攝像頭預(yù)覽的相關(guān)資料,需要的朋友可以參考下
    2025-07-07
  • SpringBoot disruptor高性能隊(duì)列使用

    SpringBoot disruptor高性能隊(duì)列使用

    這篇文章主要介紹了SpringBoot disruptor高性能隊(duì)列使用,Disruptor是英國(guó)外匯交易公司LMAX開(kāi)發(fā)的一個(gè)高性能隊(duì)列,研發(fā)的初衷是解決內(nèi)存隊(duì)列的延遲問(wèn)題
    2023-02-02
  • 詳解idea從git上拉取maven項(xiàng)目詳細(xì)步驟

    詳解idea從git上拉取maven項(xiàng)目詳細(xì)步驟

    這篇文章主要介紹了詳解idea從git上拉取maven項(xiàng)目詳細(xì)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java通過(guò)AOP實(shí)現(xiàn)全局日志打印詳解

    java通過(guò)AOP實(shí)現(xiàn)全局日志打印詳解

    最近自己一直再看現(xiàn)有微服務(wù)的日志模塊,發(fā)現(xiàn)就是使用AOP來(lái)做controller層的日志處理,加上項(xiàng)目在進(jìn)行架構(gòu)優(yōu)化,這篇文章主要給大家介紹了關(guān)于java通過(guò)AOP實(shí)現(xiàn)全局日志打印的相關(guān)資料,需要的朋友可以參考下
    2022-01-01
  • Java的RxJava庫(kù)操作符的用法及實(shí)例講解

    Java的RxJava庫(kù)操作符的用法及實(shí)例講解

    RxJava由于提供異步和基于事件的支持在Android開(kāi)發(fā)者中獲得了不少人氣,這里我們就來(lái)看一下Java的RxJava庫(kù)操作符的用法及實(shí)例講解,需要的朋友可以參考下
    2016-06-06
  • java使用JSONObject實(shí)例

    java使用JSONObject實(shí)例

    JAVA中JSONObject對(duì)象的使用方法
    2013-11-11
  • 詳解java中各類(lèi)鎖的機(jī)制

    詳解java中各類(lèi)鎖的機(jī)制

    這篇文章為大家總結(jié)了java中常見(jiàn)的鎖(互斥鎖、讀寫(xiě)鎖、公平鎖與非公平鎖等)的機(jī)制以及如何使用,文中示例代碼講解詳細(xì),需要的可以學(xué)習(xí)一下
    2021-12-12

最新評(píng)論

阳高县| 安顺市| 兴文县| 灵宝市| 湖州市| 乌鲁木齐市| 大田县| 贵定县| 肇州县| 遂川县| 四川省| 青海省| 峨边| 赣榆县| 松江区| 舒城县| 苍梧县| 类乌齐县| 县级市| 临猗县| 侯马市| 开原市| 嘉兴市| 新河县| 司法| 桐庐县| 广平县| 甘孜| 温泉县| 共和县| 宜昌市| 商都县| 福泉市| 林州市| 玛纳斯县| 南皮县| 达拉特旗| 白玉县| 砀山县| 望谟县| 巢湖市|