Java中this调用会导致事务失效的深入分析

 更新时间:2026年04月13日 08:48:55   作者:提前退休的java猿  
在Java中this是一个非常重要的关键字,它表示当前对象的引用,也就是说,当你在某个类的实例方法或构造器中时,this指向调用该方法或创建的当前对象实例,这篇文章主要介绍了Java中this调用会导致事务失效的相关资料,需要的朋友可以参考下

前言

前一两周,公司的业务出了重大问题。数据库的服务器CPU 100%然后一些重要的抽奖业务竟然出现物品超领的情况。

同事的反馈就是 一个事务里面 只执行了后半部分,也就是领取记录插入(B操作)成功了,库存扣减(A操作)是没有问题的。

核心代码如下(大家看一下事务是否失效🤶):

//----------------------------------------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) {
       // 对所有代码进行 try,然后抛出RuntimException 让框架回滚
       try{
       .............................
        //✅ A操作:库存扣减,这个地方对库存-1,如果更新行数大与0返回true 
        //UPDATE t SET num =  num -1  WHERE id = #{id} AND num > 0;
        boolean isok = reduceInventory(id);
        if (isok) {
            // ✅B操作:插入领取记录,插入成功返回 true
            Boolean insetSuc = insertRecord(record);
            if (!insetSuc) {
               throw new RuntimeException("领取失败!");
            }
            return result;
        } 
    }catch (Exception e){
        throw new RuntimeException("xxxx");
    }
}

🧔:很多网友都说this调用了reduceInventory、insertRecord方法事物不会生效

✔这种理解肯定是不对的,this调用的外层方法是加了事物注解,并且是@Resource注入调用,是能被代理的。外层方法都被拦截了代理了,方法内部再this调用也是没有问题的,默认就加入当前事务了,如果内部方法通过容器对象获取,并且有事物注解这时候就是看事物的传播机制了。

事务源码分析

下面就是事务拦截器主要的代码逻辑 TransactionAspectSupport.invokeWithinTransaction:

  • 创建或获取事务
  • 执行业务方法
  • 捕捉异常:有异常处理回滚、没有匹配到回滚异常则提交事务
  • 清理事务信息
  • 提交事务
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);
    // 处理响应式事务(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)) {
       // ✅关键步骤1:创建或获取事务(创建新事务还是沿用使用就看传播机制了)
       // Standard transaction demarcation with getTransaction and commit/rollback calls.
       TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
       Object retVal;
       try {
          // ✅关键步骤2:执行业务方法(AOP链的下一个拦截器或目标方法) 
          (执行目标方法:内部调用的方法使用this指向默认也是被拦截的,
          如果有事务注解并且通过容器对象调用,执行的时候就会再次被拦截,此时事务的传播机制的作用就体现出来了)
          // 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) {
          // ✅关键步骤3:异常处理回滚
          // target invocation exception
          completeTransactionAfterThrowing(txInfo, ex);
          throw ex;
       }
       finally {
          //✅关键步骤4:清理事务信息
          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);
          }
       }
       // ✅关键步骤5:提交事务
       commitTransactionAfterReturning(txInfo);
       return retVal;
    }
    else {
      //**JTA(Java Transaction API)**  或需要回调机制的事务管理器
      //...........................................................
    }
}

一、创建获取事务

创建事务信息createTransactionIfNecessary,如果之前已经存在事务就走需要判断事务的传播机制了

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
        @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    // 如果没有指定名称,使用方法标识作为事务名称
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }
    TransactionStatus status = null;
    if (txAttr != null) {
        // ✅关键:根据传播行为获取事务状态
        if (tm != null) {
            status = tm.getTransaction(txAttr);  // 这里处理PROPAGATION_REQUIRED等
        }
        // ...
    }
    // ✅准备事务信息
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

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

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    // 1. 获取事务定义
    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
    // 2. 获取现有事务(处理传播行为)
    Object transaction = doGetTransaction();
    // 3. 检查当前是否存在事务
    if (isExistingTransaction(transaction)) {
        // 已存在事务:根据传播行为处理
        return handleExistingTransaction(def, transaction, debugEnabled);
    }
    // 4. 没有现有事务:检查超时等设置
    if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
    }
    // 5. 需要新事务: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) {
        // 挂起现有资源(如果有)
        SuspendedResourcesHolder suspendedResources = suspend(null);
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            DefaultTransactionStatus status = newTransactionStatus(
                def, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            // 关键:开始新事务
            doBegin(transaction, def);
            prepareSynchronization(status, def);
            return status;
        }
        catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    else {
        // 6. 空事务:PROPAGATION_SUPPORTS, PROPAGATION_NOT_SUPPORTED, PROPAGATION_NEVER
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }
}

二、异常回滚

执行回滚 : 匹配指定设置的异常,匹配不到就匹配 RuntimeException 或 Error

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        // 关键:根据回滚规则决定是否回滚
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                // 执行回滚 : 匹配指定设置的异常,匹配不到就匹配 RuntimeException 或 Error
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {
                // 记录错误但不抛出
                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 {
            // 不满足回滚条件,提交事务
            try {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {
                // ... 异常处理
            }
        }
    }
}

三、为什么清理事务信息后才提交事务

在标准事务分支执行逻辑如下,没有发生异常的情况就是先清理事务信息然后提交事务:

try {
    retVal = invocation.proceedWithInvocation();  // 执行业务方法
}
catch (Throwable ex) {
    completeTransactionAfterThrowing(txInfo, ex);  // 异常回滚
    throw ex;
}
finally {
    cleanupTransactionInfo(txInfo);  // 清理事务信息
}
commitTransactionAfterReturning(txInfo);  // 提交事务

1.事务信息与事务状态分离

首先理解两个关键概念:

  • TransactionInfo:线程绑定的事务上下文信息
  • TransactionStatus:实际的事务状态(包含数据库连接、回滚标记等)
public class TransactionInfo {
    private final PlatformTransactionManager transactionManager;
    private final TransactionAttribute transactionAttribute;
    private final String joinpointIdentification;
    private TransactionStatus transactionStatus;  // 实际事务状态
    private TransactionInfo oldTransactionInfo;   // 旧的事务信息(用于嵌套事务)
}

关键点:提交事务需要的是 TransactionStatus,而不是 TransactionInfo。清理的是线程绑定的上下文信息,而不是实际的事务状态。

2. cleanupTransactionInfo 实际做了什么?

protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
    if (txInfo != null) {
        // 恢复线程的事务状态到之前的状态
        txInfo.restoreThreadLocalStatus();
    }
}
// TransactionInfo.restoreThreadLocalStatus()
public void restoreThreadLocalStatus() {
    // 将线程绑定恢复到旧的事务信息(如果有的话)
    TransactionSynchronizationManager.bindResource(
        this.transactionManager, this.oldTransactionInfo);
}

重点cleanupTransactionInfo 只是恢复线程的 ThreadLocal 状态,并不影响实际事务的连接和状态

3. 详细执行时序分析

可以看一下面事务嵌套的案例

@Service
public class UserService {
    @Transactional
    public void methodA() {
        // 事务A开始
        userRepository.updateA();
        // 调用 methodB(REQUIRES_NEW)
        userServiceProxy.methodB();  // 通过代理调用
        // 事务A继续
        userRepository.updateA2();
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 事务B开始(挂起事务A)
        logRepository.save();
        // 事务B提交
    }
}

执行时序: 大概的逻辑就是,执行完一个事务就需要立即把当前事务方法的外层方法的事务给恢复(存在的话)。

时间轴:
│
├─ 进入 methodA()
│   ├─ 创建 TransactionInfo_A(绑定到 ThreadLocal)
│   ├─ 开始事务A
│   │
│   ├─ 调用 methodB()(通过代理)
│   │   ├─ 创建 TransactionInfo_B(绑定到 ThreadLocal,保存旧的 TransactionInfo_A)
│   │   ├─ 挂起事务A
│   │   ├─ 开始事务B
│   │   ├─ 执行业务逻辑
│   │   │
│   │   ├─ 清理阶段:
│   │   │   ├─ cleanupTransactionInfo(TransactionInfo_B) 
│   │   │   │   └─ 恢复 ThreadLocal 到 TransactionInfo_A ✓
│   │   │   │
│   │   │   ├─ 提交事务B ✓
│   │   │   │   └─ 需要事务B的 TransactionStatus,但不需要 ThreadLocal 的 TransactionInfo_B
│   │   │   │
│   │   │   └─ 恢复事务A
│   │   └─ 返回
│   │
│   └─ methodA 继续执行
│       ├─ 使用 TransactionInfo_A(已恢复)
│       ├─ 提交事务A
│       └─ 清理 TransactionInfo_A
└─ 结束

4. 提交事务不需要 ThreadLocal 绑定

// AbstractPlatformTransactionManager.commit()
public final void commit(TransactionStatus status) throws TransactionException {
    // 直接从 TransactionStatus 获取实际事务对象
    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    // 检查是否需要回滚
    if (defStatus.isLocalRollbackOnly() || defStatus.isGlobalRollbackOnly()) {
        processRollback(defStatus, false);
        return;
    }
    // 实际提交
    processCommit(defStatus);
}
// DataSourceTransactionManager.doCommit()
protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    // 直接使用连接提交
    con.commit();  // 不需要 ThreadLocal 信息
}

关键:提交操作只需要:

  1. 数据库连接(从 TransactionStatus 获取)
  2. 事务状态(是否标记为回滚)

❗明白事务传播机制以及事务的相关信息是绑定到ThreadLocal中之后,用异步的时候就要注意事务失效的问题了

附:快速验证事务是否生效的方法

你可以在单元测试中这样验证(概念代码):

@SpringBootTest
class UserServiceTest {
    @Autowired
    private UserService userService;
    @Test
    void testTransactionWithThisCall() {
        // 预期:内部 this 调用时,异常不会回滚
        assertThrows(RuntimeException.class, () -> userService.addUserAndLog(new User()));
        // 检查数据库,记录应该被插入(事务未回滚)
    }
    @Test
    void testTransactionViaProxy() {
        // 使用注入的代理对象直接调用事务方法,应该回滚
        assertThrows(RuntimeException.class, () -> userService.insertUser(new User()));
        // 检查数据库,记录不应该存在
    }
}

更精确的验证可以开启 SQL 日志,观察是否打印 BEGIN 和 ROLLBACK

总结 

到此这篇关于Java中this调用会导致事务失效的文章就介绍到这了,更多相关Java this调用事务失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 利用java获取某个文件夹下的所有文件

    利用java获取某个文件夹下的所有文件

    这篇文章主要给大家介绍了关于如何利用java获取某个文件夹下的所有文件的相关资料,在从事web开发工作中,经常需要对本地某一个目录下的文件进行处理,需要的朋友可以参考下
    2023-07-07
  • 通过Docker启动Solace并在Spring Boot通过JMS整合Solace的操作方法

    通过Docker启动Solace并在Spring Boot通过JMS整合Solace的操作方法

    本文将介绍如何在Spring中使用,虽然代码使用的是Spring Boot,但并没有使用相关starter,跟Spring的整合一样,可通用,JMS是通过的消息处理框架,可以深入学习一下,不同的MQ在JMS的整合上都是类似的,感兴趣的朋友跟随小编一起看看吧
    2023-01-01
  • SpringBoot 集成 Nebula的操作过程

    SpringBoot 集成 Nebula的操作过程

    这篇文章主要介绍了SpringBoot 集成 Nebula的操作过程,通过示例代码介绍了java 环境下如何对 Nebula Graph 进行操作,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • 使用Java调用海康威视SDK实现摄像头预览超详细教程

    使用Java调用海康威视SDK实现摄像头预览超详细教程

    现在制造业很多都是用的海康的摄像头,作为程序员有时候需要对接海康摄像头,这篇文章主要介绍了使用Java调用海康威视SDK实现摄像头预览的相关资料,需要的朋友可以参考下
    2025-07-07
  • SpringBoot disruptor高性能队列使用

    SpringBoot disruptor高性能队列使用

    这篇文章主要介绍了SpringBoot disruptor高性能队列使用,Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题
    2023-02-02
  • 详解idea从git上拉取maven项目详细步骤

    详解idea从git上拉取maven项目详细步骤

    这篇文章主要介绍了详解idea从git上拉取maven项目详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • java通过AOP实现全局日志打印详解

    java通过AOP实现全局日志打印详解

    最近自己一直再看现有微服务的日志模块,发现就是使用AOP来做controller层的日志处理,加上项目在进行架构优化,这篇文章主要给大家介绍了关于java通过AOP实现全局日志打印的相关资料,需要的朋友可以参考下
    2022-01-01
  • Java的RxJava库操作符的用法及实例讲解

    Java的RxJava库操作符的用法及实例讲解

    RxJava由于提供异步和基于事件的支持在Android开发者中获得了不少人气,这里我们就来看一下Java的RxJava库操作符的用法及实例讲解,需要的朋友可以参考下
    2016-06-06
  • java使用JSONObject实例

    java使用JSONObject实例

    JAVA中JSONObject对象的使用方法
    2013-11-11
  • 详解java中各类锁的机制

    详解java中各类锁的机制

    这篇文章为大家总结了java中常见的锁(互斥锁、读写锁、公平锁与非公平锁等)的机制以及如何使用,文中示例代码讲解详细,需要的可以学习一下
    2021-12-12

最新评论