Spring 事务原理深度解析

1. 事务核心概念

1.1 什么是事务

事务是数据库操作的基本单位,它满足 ACID 四大特性:

  • Atomic(原子性):一个事务中的所有操作要么全部成功,要么全部失败回滚
  • Consistency(一致性):事务执行前后,数据库状态保持一致
  • Isolation(隔离性):并发事务之间相互隔离,不互相干扰
  • Durability(持久性):事务提交后,其结果永久保存在数据库中
sql 复制代码
BEGIN;
UPDATE account SET balance = balance - 100 WHERE user_id = 1;
UPDATE account SET balance = balance + 100 WHERE user_id = 2;
COMMIT;

1.2 Spring 事务两种开发方式

声明式事务 :使用 @Transactional 注解,配置式管理

java 复制代码
@Transactional(rollbackFor = Exception.class)
public void transferMoney(String from, String to, BigDecimal amount) {
    // 转账逻辑
}

编程式事务:手动编写事务代码,精细控制

java 复制代码
@Service
public class OrderService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void createOrder(Order order) {
        transactionTemplate.execute(status -> {
            orderMapper.insert(order);
            orderItemMapper.batchInsert(order.getItems());
            return true;
        });
    }
}

2. 事务传播行为

事务传播行为定义了事务方法的调用关系,当一个事务方法调用另一个事务方法时,如何决定事务的创建和参与方式。

Spring 定义了 7 种传播行为:

传播行为 说明
REQUIRED(默认) 如果当前有事务,加入该事务;如果没有,创建新事务
REQUIRES_NEW 总是创建新事务,挂起当前事务
SUPPORTS 如果当前有事务,加入事务;如果没有,以非事务执行
NOT_SUPPORTED 以非事务执行,挂起当前事务
MANDATORY 必须在一个事务中运行,否则抛出异常
NEVER 必须不在事务中运行,否则抛出异常
NESTED 如果当前有事务,在嵌套事务中执行;如果没有,创建新事务

2.1 REQUIRED vs REQUIRES_NEW 实战区别

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private AccountService accountService;

    @Transactional
    public void createOrder(Order order) {
        // 订单创建在当前事务中
        orderMapper.insert(order);
        
        // 扣款独立事务,即使失败也不影响订单创建
        accountService.deductMoney(order.getUserId(), order.getAmount());
        
        // 如果扣款失败抛出异常,订单已经创建成功了
        // 需要根据业务决定是否回滚
    }
}

@Service
public class AccountService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deductMoney(Long userId, BigDecimal amount) {
        // 这个方法有独立事务
        // 无论外层事务成功与否,这个事务都会提交
    }
}

2.2 NESTED 嵌套事务

NESTED 利用 Savepoint 机制实现嵌套事务:

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private InventoryService inventoryService;

    @Transactional
    public void createOrder(Order order) {
        // 插入订单 - 主事务
        orderMapper.insert(order);
        
        try {
            // 扣减库存 - 嵌套事务
            // 如果失败,只会回滚到这个保存点
            inventoryService.deduct(order.getItems());
        } catch (Exception e) {
            // 订单仍然存在,可以做补偿处理
            log.error("库存扣减失败", e);
        }
    }
}

3. 事务隔离级别

隔离级别定义了并发事务之间的可见性问题。

3.1 四种隔离级别

隔离级别 脏读 不可重复读 幻读
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE
  • 脏读:读取到其他事务未提交的数据
  • 不可重复读:同一查询在不同时间返回不同结果
  • 幻读:同一查询在不同时间返回不同数量的行

3.2 Spring 中的配置

java 复制代码
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferMoney(String from, String to, BigDecimal amount) {
    // 业务逻辑
}

3.3 MySQL 默认隔离级别

MySQL InnoDB 默认使用 REPEATABLE_READ,通过 MVCC 机制实现非阻塞读。

sql 复制代码
-- 查看当前会话隔离级别
SELECT @@transaction_isolation;

-- 查看全局隔离级别
SELECT @@global.transaction_isolation;

-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

4. @Transactional 原理

4.1 核心原理:动态代理

@Transactional 的本质是 AOP 动态代理。当 Spring 扫描到 @Transactional 注解时,会为目标类创建代理对象,在方法执行前后添加事务管理逻辑。
JDK/CGLIB 代理对象
方法返回
返回结果
目标对象
真实业务逻辑
客户端
调用 createOrder()
事务切面
调用目标方法
提交/回滚事务

4.2 代理创建流程

java 复制代码
// Spring 事务自动配置入口
@Configuration
@EnableTransactionManagement  // 启用事务管理
public class TransactionAutoConfiguration {
    
    @Bean
    public TransactionInterceptor transactionInterceptor() {
        // 创建事务拦截器
        return new TransactionInterceptor();
    }
    
    @Bean
    public BeanFactoryTransactionAttributeSourceAdvisor 
            transactionAdvisor() {
        // 将拦截器封装为 Advisor
        return new BeanFactoryTransactionAttributeSourceAdvisor();
    }
}

4.3 TransactionInterceptor 执行流程

java 复制代码
public class TransactionInterceptor extends MethodInterceptor {
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 1. 获取事务属性
        TransactionAttributeSource tas = getTransactionAttributeSource();
        TransactionAttribute ta = tas.getTransactionAttribute(
            invocation.getMethod(), invocation.getThis());
        
        // 2. 获取事务管理器
        PlatformTransactionManager tm = determineTransactionManager(ta);
        
        // 3. 执行事务
        return invokeWithinTransaction(
            invocation.getMethod(), 
            invocation.getThis(), 
            ta, 
            invocation::proceed
        );
    }
    
    protected Object invokeWithinTransaction(
            Method method, 
            Object target, 
            TransactionAttribute ta,
            InvocationCallback invocation) throws Throwable {
        
        // 获取事务
        TransactionInfo txInfo = createTransactionIfNecessary(ta, method);
        
        try {
            // 执行目标方法
            Object result = invocation.proceedWithInvocation();
            
            // 提交事务
            commitTransactionAfterReturning(txInfo);
            return result;
            
        } catch (Throwable ex) {
            // 回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        } finally {
            // 清理事务信息
            cleanupTransactionInfo(txInfo);
        }
    }
}

4.4 事务管理器获取

java 复制代码
protected PlatformTransactionManager determineTransactionManager(
        TransactionAttribute txAttr) {
    
    // 1. 按 beanName 查找
    if (txAttr != null && txAttr.getQualifier() != null) {
        return lookupTransactionManager(txAttr.getQualifier());
    }
    
    // 2. 按 bean 类型查找
    if (getTransactionManagerBeanNames().length > 0) {
        Object tm = getTransactionManagerBeanNames().length == 1 
            ? getDefaultTransactionManager() 
            : getTransactionManagerBeanNames();
        return lookupTransactionManager(tm);
    }
    
    // 3. 返回默认事务管理器
    return getDefaultTransactionManager();
}

5. @Transactional 失效场景

5.1 同一个类中方法调用

java 复制代码
@Service
public class OrderService {

    @Transactional
    public void createOrder(Order order) {
        // 开启事务
        orderMapper.insert(order);
        
        // this 调用不会触发代理,事务无效
        this.processPayment(order);
    }
    
    @Transactional
    public void processPayment(Order order) {
        // 不会开启新事务
        // 使用当前事务(如果有)
        paymentMapper.update(order);
    }
}

解决方案:注入自身或使用 AopContext

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderService self;  // 注入自身
    
    @Transactional
    public void createOrder(Order order) {
        orderMapper.insert(order);
        
        // 通过代理对象调用,事务生效
        self.processPayment(order);
    }
}

5.2 非 public 方法

java 复制代码
@Service
public class OrderService {

    // 事务无效 - protected/private 方法
    @Transactional
    void createOrderInternal(Order order) {
        orderMapper.insert(order);
    }
}

Spring 事务基于 AOP,代理方法必须是 public。

5.3 异常被捕获

java 复制代码
@Transactional
public void createOrder(Order order) {
    try {
        orderMapper.insert(order);
    } catch (Exception e) {
        // 异常被捕获,事务不会回滚
        log.error("创建订单失败", e);
    }
}

解决方案:配置异常回滚

java 复制代码
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) {
    try {
        orderMapper.insert(order);
    } catch (Exception e) {
        // 手动回滚
        TransactionAspectSupport.currentTransactionStatus()
            .setRollbackOnly();
        throw new BusinessException("创建订单失败", e);
    }
}

5.4 传播行为配置错误

java 复制代码
// 当前方法在 NOT_SUPPORTED 事务中
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void createOrder(Order order) {
    // 不会开启事务
    orderMapper.insert(order);
}

5.5 数据库不支持事务

java 复制代码
// MyISAM 存储引擎不支持事务
@Transactional
public void createOrder(Order order) {
    // 事务不会生效
    orderMapper.insert(order);
}

5.6 多数据源事务

多数据源情况下,Spring 默认只支持单数据源事务。多数据源事务需要使用 JTA 或 Seata。

java 复制代码
// 使用 Seata 分布式事务
@GlobalTransactional
public void createOrder(Order order) {
    // 订单库
    orderMapper.insert(order);
    // 库存库(远程)
    remoteInventoryService.deduct(order.getItems());
    // 支付库(远程)
    remotePaymentService.pay(order.getPayment());
}

6. TransactionTemplate 编程式事务

6.1 基本用法

java 复制代码
@Service
public class OrderService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void createOrder(Order order) {
        transactionTemplate.execute(status -> {
            orderMapper.insert(order);
            orderItemMapper.batchInsert(order.getItems());
            
            // 手动标记回滚
            // status.setRollbackOnly();
            
            return true;
        });
    }
}

6.2 带返回值和超时

java 复制代码
public BigDecimal calculateOrderTotal(Long orderId) {
    return transactionTemplate.execute(status -> {
        status.setTimeout(30);  // 30秒超时
        
        Order order = orderMapper.selectById(orderId);
        List<OrderItem> items = orderItemMapper.selectByOrderId(orderId);
        
        return items.stream()
            .map(OrderItem::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    });
}

6.3 声明式 vs 编程式

特性 声明式事务 编程式事务
代码侵入
精细控制
学习成本
适用场景 简单业务 复杂事务逻辑

7. Spring 事务底层实现

7.1 TransactionSynchronizationManager

Spring 通过 TransactionSynchronizationManager 管理线程本地的事务状态:

java 复制代码
public abstract class TransactionSynchronizationManager {
    
    // 事务资源(Connection、Session 等)
    private static final ThreadLocal<Map<Object, Object>> resources =
        new NamedThreadLocal<>("Transactional resources");
    
    // 当前事务信息
    private static final ThreadLocal<TransactionInfo> currentTransactionInfo =
        new NamedThreadLocal<>("Current transaction info");
    
    // 事务同步回调
    private static final ThreadLocal<Set<TransactionSynchronization>> 
        synchronizations = new NamedThreadLocal<>("Transaction synchronizations");
}

7.2 事务执行流程

数据库 TransactionManager 代理对象 客户端 数据库 TransactionManager 代理对象 客户端 createOrder() getTransaction() 获取数据库连接 开启事务(关闭自动提交) 返回 TransactionStatus 执行目标方法 执行 SQL commit() 提交事务 提交成功 返回 返回结果

7.3 事务同步回调

java 复制代码
@Transactional
public void createOrder(Order order) {
    // 注册事务完成后执行的回调
    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                // 事务提交后发送消息通知
                messageQueue.send(order);
            }
            
            @Override
            public void afterCompletion(int status) {
                // 事务完成后(无论成功或失败)
                log.info("事务完成, status: {}", status);
            }
        }
    );
    
    orderMapper.insert(order);
}

8. 最佳实践

8.1 事务范围最小化

java 复制代码
// 不推荐:事务范围过大
@Transactional
public void createOrder(Order order) {
    // 1. 校验参数(不需要事务)
    validateOrder(order);
    
    // 2. 创建订单(需要事务)
    orderMapper.insert(order);
    
    // 3. 发送消息(不需要事务,可能导致长事务)
    messageQueue.send(order);
}

// 推荐:拆分事务边界
public void createOrder(Order order) {
    validateOrder(order);
    
    // 只在必要的地方加事务
    transactionTemplate.execute(status -> {
        orderMapper.insert(order);
        return null;
    });
    
    // 事务外执行
    messageQueue.send(order);
}

8.2 异常处理规范

java 复制代码
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) {
    try {
        orderMapper.insert(order);
    } catch (DuplicateKeyException e) {
        // 业务异常,手动回滚
        TransactionAspectSupport.currentTransactionStatus()
            .setRollbackOnly();
        throw new BusinessException("订单号重复");
    }
}

8.3 只读事务优化

java 复制代码
@Transactional(readOnly = true)
public List<Order> listOrders() {
    // 告诉数据库优化查询
    // 可能开启读写分离的从库
    return orderMapper.selectList(null);
}

9. 总结

Spring 事务是 Spring Framework 的核心功能之一,理解其原理对开发高质量应用至关重要。

  • 传播行为 决定了事务方法之间的协作方式,REQUIRED 是最常用的
  • @Transactional 基于动态代理实现,同一个类中调用会失效
  • TransactionTemplate 提供更精细的事务控制,适合复杂业务场景
  • 失效场景需要特别注意:同类调用、非 public 方法、异常捕获、传播行为配置等

掌握这些知识点,能够帮助你更好地应对面试和实际开发中的事务问题。

相关推荐
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于SpringBoot的健康系统为例,包含答辩的问题和答案
java·spring boot·后端
曹牧2 小时前
@RequestBody 注解处理的数据类型
java
xixihaha13242 小时前
实战:用OpenCV和Python进行人脸识别
jvm·数据库·python
慧都小项2 小时前
Java开发工具MyEclipse发布v2026.1:支持Java25和Spring Boot4、AI功能升级
java·spring boot·myeclipse
L0CK2 小时前
实战篇 01. 达人探店 - 发布探店笔记学习文档
java
云边有个稻草人2 小时前
Oracle替换工程实践深度解析:金仓数据库的“去O”攻坚之路
数据库
独断万古他化2 小时前
【抽奖系统开发实战】Spring Boot 项目的用户模块设计:注册登录、权限管控与敏感数据加密
java·spring boot·redis·后端·mvc·jwt·拦截器
泯仲2 小时前
从零起步学习MySQL 第十三章:MySQL 事务详解:原理、特性、并发问题与隔离级别
数据库·学习·mysql
一直学习的程序小白2 小时前
java进阶-优化GC垃圾回收机制
java·开发语言·jvm