Spring - 事务管理

一、Spring 事务概览

1.1 什么是 Spring 事务管理?

Spring 事务管理是对数据库事务的抽象封装,通过 AOP 实现声明式事务,让开发者无需手动编写 Connection.setAutoCommit(false) / commit() / rollback() 等代码。

Spring 提供两种事务管理方式:

方式 说明 使用场景
声明式事务 基于 AOP,通过注解或 XML 配置 绝大多数场景
编程式事务 手动控制事务边界 需要细粒度控制的场景

1.2 Spring 事务抽象架构

flowchart TB subgraph 应用层 A[业务代码] B["@Transactional 注解"] end subgraph Spring事务抽象层 C[PlatformTransactionManager
事务管理器接口] D[TransactionDefinition
事务定义] E[TransactionStatus
事务状态] end subgraph 具体实现 F[DataSourceTransactionManager] G[JpaTransactionManager] H[JtaTransactionManager] end subgraph 底层资源 I[(数据库连接)] end A --> C B --> C C --> D C --> E C --> F C --> G C --> H F --> I G --> I

核心三接口

接口 作用
PlatformTransactionManager 事务管理器,负责获取事务、提交、回滚
TransactionDefinition 定义事务属性(传播、隔离、超时、只读)
TransactionStatus 事务运行状态,可查询是否新事务、是否已完成
java 复制代码
public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}

1.3 @Transactional 注解属性

java 复制代码
@Transactional(
    propagation = Propagation.REQUIRED,       // 传播行为,默认 REQUIRED
    isolation = Isolation.DEFAULT,            // 隔离级别,默认 DEFAULT(使用数据库默认)
    timeout = -1,                             // 超时时间(秒),默认 -1(无超时)
    readOnly = false,                         // 是否只读,默认 false
    rollbackFor = {},                         // 指定回滚异常(数组)
    noRollbackFor = {},                       // 指定不回滚异常(数组)
    transactionManager = "",                  // 指定事务管理器
    label = {}                                // 事务标签
)

默认回滚策略 :只有 RuntimeExceptionError 触发回滚,受检异常(checked exception)不回滚。这是面试高频考点。


二、@Transactional 原理

2.1 整体原理:AOP + ThreadLocal

@Transactional 本质是一个 AOP 切面,Spring 在 Bean 创建阶段通过 AutoProxyCreator 为标注了 @Transactional 的 Bean 创建代理对象。

flowchart TB A[调用代理对象方法] --> B{是否存在事务?} B -->|不存在| C[开启事务
conn.setAutoCommit false] B -->|已存在| D[根据传播行为决定
加入/挂起/新建] C --> E[执行目标方法] D --> E E --> F{是否抛出异常?} F -->|RuntimeException/Error| G[回滚 rollback] F -->|checked exception| H[提交 commit] F -->|正常返回| H

2.2 核心源码:TransactionInterceptor

事务切面的核心是 TransactionInterceptor,它实现了 MethodInterceptor 接口:

java 复制代码
// org.springframework.transaction.interceptor.TransactionInterceptor

public class TransactionInterceptor extends TransactionAspectSupport
        implements MethodInterceptor, Serializable {

    @Override
    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = invocation.getThis() != null
                ? AopUtils.getTargetClass(invocation.getThis()) : null;

        // 调用父类 TransactionAspectSupport 的核心方法
        return invokeWithinTransaction(invocation.getMethod(), targetClass,
                invocation::proceed);
    }
}

真正的逻辑在父类 TransactionAspectSupport.invokeWithinTransaction() 中:

java 复制代码
// org.springframework.transaction.interceptor.TransactionAspectSupport

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
        InvocationCallback invocation) throws Throwable {

    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = tas.getTransactionAttribute(method, targetClass);

    final PlatformTransactionManager tm = determineTransactionManager(txAttr);

    // 1. 解析事务名称
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    // 2. 声明式事务处理(注解方式)
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

        Object retVal;
        try {
            // 3. 执行目标方法
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable ex) {
            // 4. 异常处理 - 判断是否需要回滚
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        } finally {
            // 5. 清理事务信息
            cleanupTransactionInfo(txInfo);
        }
        // 6. 正常返回 - 提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    // ... 编程式事务处理(CallbackPreferringPlatformTransactionManager)
}

2.3 事务如何绑定到线程?ThreadLocal

Spring 通过 ThreadLocal 将数据库连接绑定到当前线程,保证同一线程内多次数据库操作使用同一个 Connection:

java 复制代码
// org.springframework.transaction.support.TransactionSynchronizationManager

public abstract class TransactionSynchronizationManager {

    // 用 ThreadLocal 保存当前线程的事务资源(如 Connection)
    private static final ThreadLocal<Map<Object, Object>> resources =
            new NamedThreadLocal<>("Transactional resources");

    // 用 ThreadLocal 保存当前线程的事务同步回调
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
            new NamedThreadLocal<>("Transaction synchronizations");

    // 用 ThreadLocal 保存当前事务的名称
    private static final ThreadLocal<String> currentTransactionName =
            new NamedThreadLocal<>("Current transaction name");

    // 用 ThreadLocal 保存当前事务是否只读
    private static final ThreadLocal<Boolean> currentTransactionReadOnly =
            new NamedThreadLocal<>("Current transaction read-only status");

    // 用 ThreadLocal 保存事务隔离级别
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
            new NamedThreadLocal<>("Current transaction isolation level");

    // 用 ThreadLocal 保存事务是否活跃
    private static final ThreadLocal<Boolean> actualTransactionActive =
            new NamedThreadLocal<>("Actual transaction active");
}
flowchart LR T[Thread-0] --> TL[ThreadLocal] TL --> C1[Connection-1
autoCommit=false] T2[Thread-1] --> TL2[ThreadLocal] TL2 --> C2[Connection-2
autoCommit=false] T3[Thread-2] --> TL3[ThreadLocal] TL3 --> C3[无事务
autoCommit=true]

关键点 :事务绑定在 ThreadLocal 上,意味着事务只能在同一个线程 内传播。如果方法内部通过 new Thread() 或线程池执行数据库操作,新线程不会继承事务。

2.4 createTransactionIfNecessary 核心流程

java 复制代码
// TransactionAspectSupport#createTransactionIfNecessary

protected TransactionInfo createTransactionIfNecessary(
        PlatformTransactionManager tm, TransactionAttribute txAttr,
        final String joinpointIdentification) {

    // 如果没有指定事务名称,使用方法标识
    if (txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }

    TransactionStatus status = null;
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 核心入口:根据事务定义获取/创建事务
        status = tm.getTransaction(txAttr);
    }

    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

getTransaction()AbstractPlatformTransactionManager 实现,核心逻辑是根据传播行为决定事务的处理方式:

java 复制代码
// org.springframework.transaction.support.AbstractPlatformTransactionManager

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {

    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

    Object transaction = doGetTransaction();
    boolean debugEnabled = logger.isDebugEnabled();

    // 已存在事务时的处理
    if (isExistingTransaction(transaction)) {
        return handleExistingTransaction(def, transaction, debugEnabled);
    }

    // 检查超时设置
    if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
    }

    // 当前没有事务,根据传播行为处理
    // MANDATORY: 必须有事务,没有则抛异常
    if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException("No existing transaction found for MANDATORY");
    }
    // REQUIRED / REQUIRES_NEW / NESTED: 没有事务则新建
    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);
            // 开启事务(如 connection.setAutoCommit(false))
            doBegin(transaction, def);
            prepareSynchronization(status, def);
            return status;
        } catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    else {
        // SUPPORTS / NOT_SUPPORTED / NEVER: 非事务方式执行
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }
}

三、事务传播机制

3.1 七种传播行为

flowchart TB subgraph 有事务时的行为 R[REQUIRED
加入当前事务] S[SUPPORTS
加入当前事务] M[MANDATORY
加入当前事务] RN[REQUIRES_NEW
挂起旧事务
创建新事务] NS[NOT_SUPPORTED
挂起当前事务
非事务执行] N[NEVER
抛出异常] NE[NESTED
创建嵌套事务
savepoint] end
传播行为 外层无事务 外层有事务 使用频率
REQUIRED(默认) 新建事务 加入外层事务 ⭐⭐⭐⭐⭐
REQUIRES_NEW 新建事务 挂起外层,新建独立事务 ⭐⭐⭐⭐
NESTED 新建事务 创建嵌套事务(savepoint) ⭐⭐⭐
SUPPORTS 非事务执行 加入外层事务 ⭐⭐⭐
NOT_SUPPORTED 非事务执行 挂起外层,非事务执行 ⭐⭐
MANDATORY 抛出异常 加入外层事务 ⭐⭐
NEVER 非事务执行 抛出异常

3.2 REQUIRED vs REQUIRES_NEW vs NESTED(高频对比)

css 复制代码
flowchart TD
    A[外层方法 @Transactional] --> B[内层方法]
    B --> C{传播行为}

    C -->|REQUIRED| D[内外层共用同一个事务<br/>一起提交/回滚]
    C -->|REQUIRES_NEW| E[挂起外层事务<br/>内层独立事务<br/>内层异常不影响外层]
    C -->|NESTED| F[创建 savepoint<br/>内层回滚到 savepoint<br/>外层事务不受影响]

代码示例

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private LogService logService;

    @Transactional
    public void createOrder(Order order) {
        orderMapper.insert(order);
        logService.recordLog(order.getId());
    }
}

@Service
public class LogService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordLog(Long orderId) {
        // 即使外层回滚,日志也会提交
    }
}

REQUIRED 场景分析

java 复制代码
@Service
public class UserService {

    @Autowired
    private UserService self; // 注入代理对象

    @Transactional
    public void methodA() {
        // methodB 和 methodC 在同一个事务中
        self.methodB();
        self.methodC(); // methodC 抛异常,整个事务回滚,methodB 的操作也回滚
    }

    @Transactional
    public void methodB() {
        // 操作数据库
    }

    @Transactional
    public void methodC() {
        throw new RuntimeException("出错了");
    }
}

REQUIRES_NEW 场景分析

java 复制代码
@Service
public class UserService {

    @Transactional
    public void methodA() {
        // 操作1
        self.methodB(); // REQUIRES_NEW 独立事务,正常提交
        // 操作2
        throw new RuntimeException(); // 只有 methodA 的事务回滚,methodB 不受影响
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 独立事务,独立提交/回滚
    }
}

3.3 REQUIRES_NEW 的底层原理

java 复制代码
// AbstractPlatformTransactionManager#handleExistingTransaction(关键片段)

case TransactionDefinition.PROPAGATION_REQUIRES_NEW:
    // 1. 挂起当前事务
    SuspendedResourcesHolder suspendedResources = suspend(transaction);
    try {
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        DefaultTransactionStatus status = newTransactionStatus(
                def, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        // 2. 开启全新事务(从连接池获取新的 Connection)
        doBegin(transaction, def);
        prepareSynchronization(status, def);
        return status;
    } catch (RuntimeException | Error beginEx) {
        // 3. 新事务开启失败,恢复挂起的事务
        resume(null, suspendedResources);
        throw beginEx;
    }

关键点suspend() 会将当前事务的资源(Connection)从 ThreadLocal 解绑并保存,然后 doBegin() 从数据源获取新的 Connection 绑定到 ThreadLocal。

3.4 NESTED 嵌套事务原理

java 复制代码
case TransactionDefinition.PROPAGATION_NESTED:
    if (useSavepointForNestedTransaction()) {
        // 默认使用 savepoint 方式
        DefaultTransactionStatus status = prepareTransactionStatus(
                def, transaction, false, false, debugEnabled, null);
        // 创建 savepoint
        status.createAndHoldSavepoint();
        return status;
    } else {
        // JTA 事务管理器可能不支持 savepoint,使用新建事务
        return handleExistingTransaction(def, transaction, debugEnabled);
    }

NESTED 与 REQUIRES_NEW 的区别 :NESTED 是同一个物理事务 中的 savepoint,回滚只回滚到 savepoint;REQUIRES_NEW 是完全独立的新事务,使用不同的 Connection。


四、事务隔离级别

4.1 四种隔离级别

隔离级别 脏读 不可重复读 幻读 性能
READ_UNCOMMITTED 可能 可能 可能 最高
READ_COMMITTED 不可能 可能 可能
REPEATABLE_READ(MySQL默认) 不可能 不可能 可能
SERIALIZABLE 不可能 不可能 不可能 最低

4.2 Spring 隔离级别与数据库隔离级别关系

java 复制代码
public interface TransactionDefinition {
    int ISOLATION_DEFAULT = -1;          // 使用数据库默认隔离级别
    int ISOLATION_READ_UNCOMMITTED = 1;  // 对应 Connection.TRANSACTION_READ_UNCOMMITTED
    int ISOLATION_READ_COMMITTED = 2;    // 对应 Connection.TRANSACTION_READ_COMMITTED
    int ISOLATION_REPEATABLE_READ = 4;   // 对应 Connection.TRANSACTION_REPEATABLE_READ
    int ISOLATION_SERIALIZABLE = 8;      // 对应 Connection.TRANSACTION_SERIALIZABLE
}

注意 :Spring 的隔离级别最终会映射为 java.sql.Connection 的常量。当设置 ISOLATION_DEFAULT 时,使用数据库自身的默认隔离级别(MySQL 默认 REPEATABLE_READ,PostgreSQL 默认 READ_COMMITTED)。

4.3 readOnly 属性

java 复制代码
@Transactional(readOnly = true)

作用

  1. 语义声明:告诉开发者和管理员这是一个只读操作
  2. 优化提示 :Spring 会向 JDBC Driver 发送 readOnly 提示,部分驱动会做优化(如 MySQL InnoDB 对只读事务不做 change buffer)
  3. 异常检查 :部分 JPA Provider(如 Hibernate)在 readOnly=true 时会跳过脏检查,提升查询性能

注意readOnly 只适用于 REQUIREDREQUIRES_NEW 传播行为,对其他传播行为无效。


五、事务失效场景(重点)

5.1 事务失效的 7 大场景

java 复制代码
mindmap
  root((事务失效场景))
    自调用问题
      同类方法内部调用
      未通过代理对象调用
    异常类型错误
      捕获了异常未抛出
      抛出的是 checked exception
    方法修饰符问题
      方法是 private
      方法是 final
      方法是 static
    非public方法
      protected / package-private
    异常被吞掉
      try-catch 未抛出
      未设置 rollbackFor
    多线程调用
      新线程无法继承事务
    传播行为不对
      NOT_SUPPORTED
      NEVER
    数据库引擎不支持
      MyISAM 不支持事务

5.2 场景一:自调用(最常见)

java 复制代码
@Service
public class UserService {

    // ❌ 事务失效!内部调用不经过代理
    public void batchInsert() {
        this.insertUser("张三");
        this.insertUser("李四"); // 异常后,张三已经插入,无法回滚
    }

    @Transactional
    public void insertUser(String name) {
        userMapper.insert(name);
        if ("李四".equals(name)) {
            throw new RuntimeException();
        }
    }
}

原因batchInsert() 内部通过 this 调用 insertUser(),绕过了代理对象,@Transactional 切面没有生效。

解决方案

java 复制代码
@Service
public class UserService {

    @Autowired
    private UserService self; // 注入自身的代理对象

    // ✅ 通过代理对象调用,事务生效
    public void batchInsert() {
        self.insertUser("张三");
        self.insertUser("李四");
    }

    @Transactional
    public void insertUser(String name) {
        userMapper.insert(name);
        if ("李四".equals(name)) {
            throw new RuntimeException();
        }
    }
}

原理 :Spring AOP 的代理对象拦截方法调用,this.insertUser() 直接调用目标对象,不经过代理。self.insertUser() 通过注入的代理对象调用,会被 TransactionInterceptor 拦截。

5.3 场景二:异常被捕获

java 复制代码
@Service
public class UserService {

    // ❌ 事务失效!异常被 try-catch 吞掉了
    @Transactional
    public void createUser(String name) {
        try {
            userMapper.insert(name);
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("创建用户失败", e);
            // 异常被吞掉,TransactionInterceptor 看不到异常,执行 commit
        }
    }
}

解决方案

java 复制代码
// 方案1:抛出异常
@Transactional
public void createUser(String name) {
    try {
        userMapper.insert(name);
        int i = 1 / 0;
    } catch (Exception e) {
        log.error("创建用户失败", e);
        throw e; // 重新抛出,让 TransactionInterceptor 处理
    }
}

// 方案2:手动标记回滚
@Transactional
public void createUser(String name) {
    try {
        userMapper.insert(name);
        int i = 1 / 0;
    } catch (Exception e) {
        log.error("创建用户失败", e);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

5.4 场景三:抛出的是 checked exception

java 复制代码
@Service
public class UserService {

    // ❌ 事务失效!IOException 是 checked exception,默认不回滚
    @Transactional
    public void createUser(String name) throws IOException {
        userMapper.insert(name);
        throw new IOException("文件写入失败");
    }
}

解决方案

java 复制代码
// 方案1:指定 rollbackFor
@Transactional(rollbackFor = Exception.class)
public void createUser(String name) throws IOException {
    userMapper.insert(name);
    throw new IOException("文件写入失败");
}

// 方案2:指定具体的异常类
@Transactional(rollbackFor = {IOException.class, SQLException.class})
public void createUser(String name) throws IOException {
    userMapper.insert(name);
    throw new IOException("文件写入失败");
}

最佳实践 :建议所有 @Transactional 都显式指定 rollbackFor = Exception.class,避免踩坑。

5.5 场景四:方法不是 public

java 复制代码
@Service
public class UserService {

    // ❌ 事务失效!@Transactional 只能用于 public 方法
    @Transactional
    void createUser(String name) {
        userMapper.insert(name);
    }
}

原因 :Spring AOP 默认只拦截 public 方法。在 AbstractFallbackTransactionAttributeSource 中有检查:

java 复制代码
// TransactionAttributeSource 的查找逻辑
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    return null; // 非 public 方法返回 null,表示没有事务属性
}

5.6 场景五:方法是 final 或 static

java 复制代码
@Service
public class UserService {

    // ❌ 事务失效!final 方法不能被重写,CGLIB 代理无法生效
    @Transactional
    public final void createUser(String name) {
        userMapper.insert(name);
    }

    // ❌ 事务失效!static 方法不属于对象实例,无法被代理
    @Transactional
    public static void staticMethod() {
        // ...
    }
}

原因 :CGLIB 通过生成子类重写方法实现代理,final 方法无法被重写;static 方法属于类而非实例,无法被 AOP 拦截。

5.7 场景六:未被 Spring 管理

java 复制代码
// ❌ 事务失效!没有加 @Component/@Service 等注解
public class UserService {

    @Transactional
    public void createUser(String name) {
        userMapper.insert(name);
    }
}

原因:类没有被 Spring 容器管理,自然不会被 AOP 代理。

5.7 场景七:数据库引擎不支持事务

sql 复制代码
-- MySQL 的 MyISAM 引擎不支持事务
CREATE TABLE user (
    id BIGINT PRIMARY KEY,
    name VARCHAR(50)
) ENGINE=MyISAM;

-- 必须使用 InnoDB 引擎
CREATE TABLE user (
    id BIGINT PRIMARY KEY,
    name VARCHAR(50)
) ENGINE=InnoDB;

5.8 事务失效场景速查表

序号 场景 是否失效 解决方案
1 自调用(this.method()) ❌ 失效 注入 self 代理对象调用
2 异常被 try-catch 吞掉 ❌ 失效 重新抛出或 setRollbackOnly()
3 抛出 checked exception ❌ 失效 rollbackFor = Exception.class
4 方法不是 public ❌ 失效 改为 public
5 方法是 final ❌ 失效 去掉 final
6 方法是 static ❌ 失效 去掉 static
7 类未被 Spring 管理 ❌ 失效 加 @Service/@Component
8 数据库引擎不支持 ❌ 失效 使用 InnoDB
9 传播行为为 NOT_SUPPORTED ❌ 失效 修改传播行为
10 多线程调用 ❌ 失效 使用编程式事务或消息队列

六、编程式事务

6.1 TransactionTemplate

java 复制代码
@Service
public class UserService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserMapper userMapper;

    public void createUser(String name) {
        transactionTemplate.execute(status -> {
            try {
                userMapper.insert(name);
                // 其他操作...
                return true;
            } catch (Exception e) {
                status.setRollbackOnly(); // 标记回滚
                return false;
            }
        });
    }
}

6.2 PlatformTransactionManager 手动控制

java 复制代码
@Service
public class UserService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void createUser(String name) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            userMapper.insert(name);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

6.3 声明式 vs 编程式

对比项 声明式事务 编程式事务
实现方式 AOP 代理 手动编码
代码侵入
粒度控制 方法级别 代码块级别
可读性
使用场景 绝大多数场景 需要细粒度控制

七、自检

Q1: @Transactional 的底层原理?

markdown 复制代码
基于 AOP + ThreadLocal 实现。

1. Spring 启动时,通过 AutoProxyCreator 为标注 @Transactional 的 Bean 创建代理对象
2. 调用方法时,代理对象的 TransactionInterceptor 拦截调用
3. 从 TransactionAttributeSource 解析事务属性(传播、隔离、超时等)
4. 通过 PlatformTransactionManager 获取/创建事务(ThreadLocal 绑定 Connection)
5. 执行目标方法
6. 正常返回则提交,抛出 RuntimeException/Error 则回滚
7. finally 中清理 ThreadLocal 中的事务信息

Q2: Spring 事务传播机制有哪些?最常用的是哪几个?

diff 复制代码
共 7 种传播行为:

最常用的 3 个:
- REQUIRED(默认):有事务就加入,没有就新建。绝大多数场景使用
- REQUIRES_NEW:总是新建独立事务,挂起外层事务。用于记录日志等独立操作
- NESTED:嵌套事务,基于 savepoint。内层回滚不影响外层

其他 4 个:
- SUPPORTS:有事务就加入,没有就非事务执行
- NOT_SUPPORTED:非事务执行,挂起当前事务
- MANDATORY:必须在事务中调用,否则抛异常
- NEVER:必须不在事务中调用,否则抛异常

Q3: REQUIRED 和 REQUIRES_NEW 的区别?

markdown 复制代码
核心区别:

1. REQUIRED:内外层共用同一个物理事务(同一个 Connection)
   - 内层回滚 → 外层也回滚
   - 外层回滚 → 内层也回滚

2. REQUIRES_NEW:挂起外层事务,创建新的独立事务(新 Connection)
   - 内层回滚 → 不影响外层
   - 外层回滚 → 不影响内层(内层已独立提交)

典型使用场景:
- REQUIRES_NEW 适用于:操作日志记录(即使主业务回滚,日志也要保留)

Q4: 说一下事务失效的场景?

kotlin 复制代码
最常见的几种:

1. 自调用:同一个类中,非事务方法调用事务方法,this 引用绕过了代理
2. 异常被吞:try-catch 捕获异常后没有重新抛出
3. 异常类型不对:checked exception 默认不回滚,需要 rollbackFor = Exception.class
4. 方法修饰符问题:非 public、final、static 方法都无法被代理
5. 类未被 Spring 管理:没有加 @Service 等注解
6. 多线程:新线程无法继承 ThreadLocal 中的事务
7. 数据库引擎:MyISAM 不支持事务

解决方案:
- 自调用 → 注入自身代理对象(@Autowired private XxxService self)
- checked exception → rollbackFor = Exception.class
- 其他 → 代码规范

Q5: 为什么 checked exception 默认不回滚?

php 复制代码
这是设计选择,Spring 认为受检异常(checked exception)通常是业务可预期的异常,
调用方已经通过 try-catch 处理了,不应该触发事务回滚。

而 RuntimeException(运行时异常)通常是不可预期的系统错误,
如 NullPointerException、IllegalArgumentException 等,应该触发回滚。

但实际开发中建议统一指定 rollbackFor = Exception.class,
让所有异常都回滚,避免踩坑。

Q6: 事务和锁的关系?

diff 复制代码
事务(Transaction)和锁(Lock)是两个不同层面的概念:

- 事务:保证一组操作的原子性(要么全成功,要么全失败)
- 锁:保证并发访问时数据的一致性

关系:
- 事务依赖锁来实现隔离级别
- 在 REPEATABLE_READ 下,MySQL 通过 MVCC + Next-Key Lock 实现可重复读
- 事务持有锁的时间 = 从事务第一条 SQL 到事务结束(commit/rollback)
- 事务时间越长,持有锁的时间越长,并发性能越差

所以事务应该尽量短,避免长事务导致锁竞争。

Q7: 如何避免长事务?

scss 复制代码
1. 不要在事务中做非数据库操作:如 RPC 调用、文件操作、复杂计算
2. 不要在事务中查询大量数据
3. 将查询操作移到事务外部
4. 使用编程式事务缩小事务范围
5. 设置合理的超时时间 timeout

示例:
// ❌ 长事务
@Transactional
public void order() {
    createOrder();        // DB操作
    rpcCall();           // 网络调用,可能耗时数秒
    sendEmail();         // 发邮件,可能耗时数秒
    updateStock();       // DB操作
}

// ✅ 短事务
public void order() {
    Order order = createOrder();
    rpcCall();
    sendEmail(order);
    updateStockInTransaction(order);
}

@Transactional
public void updateStockInTransaction(Order order) {
    updateStock();
}

核心源码路径速查

类/方法 作用
TransactionInterceptor#invoke() 事务切面入口,拦截方法调用
TransactionAspectSupport#invokeWithinTransaction() 事务执行核心逻辑
TransactionAspectSupport#createTransactionIfNecessary() 创建/获取事务
AbstractPlatformTransactionManager#getTransaction() 根据传播行为决定事务处理
AbstractPlatformTransactionManager#handleExistingTransaction() 已有事务时的处理
AbstractPlatformTransactionManager#suspend() 挂起事务
AbstractPlatformTransactionManager#commit() 提交事务
AbstractPlatformTransactionManager#rollback() 回滚事务
TransactionSynchronizationManager 事务资源与线程绑定(ThreadLocal)
DataSourceTransactionManager#doBegin() 开启事务(获取 Connection,setAutoCommit(false))

如果这篇文章对你有一点点启发,请不要吝啬你的点赞关注 。你的每一个关注 都是我持续更新的最大动力!🙏,持续分享技术笔记,更多文章请访问我的博客 gelald.dpdns.org/

相关推荐
nghxni2 小时前
LightESB Timer发布:服务级日志与响应编码增强
后端
橘子编程2 小时前
编译原理:从理论到实战全解析
java·linux·python·ubuntu
xuhaoyu_cpp_java2 小时前
Maven学习(一)
java·经验分享·笔记·学习·maven
Southern Wind2 小时前
AI Skill Server 动态技能中台
前端·后端·mysql·node.js
sibylyue2 小时前
Nginx\Tomcat\Jetty\Netty
java·nginx·http
于先生吖2 小时前
基于 SpringBoot 架构,高性能 JAVA 动漫短剧系统源码
java·开发语言·spring boot
chen_ever2 小时前
从网络基础到吃透 Linux 高并发 I/O 核心(epoll+零拷贝 完整版)
linux·网络·c++·后端
斌味代码2 小时前
SpringBoot 3 实战:虚拟线程、全局异常处理与 JWT 鉴权完整方案
java·spring boot·后端
电商API&Tina2 小时前
跨境电商如何接入1688官方寻源通接口?附接入流程
java·数据库·python·sql·oracle·json·php