公司动态

@Transactional 原理与事务失效场景

📅 2026/7/15 20:23:17
@Transactional 原理与事务失效场景
一句话Transactional 基于 AOP 动态代理通过 TransactionInterceptor 在方法前后开启/提交/回滚事务。自调用绕过代理、异常被吞、非 public 方法等 6 个场景会导致事务失效。1. 底层原理Controller 调用 userService.save() ↓ 实际调用的是 userService 的代理对象AOP 动态代理生成的 ↓ 代理对象先走 TransactionInterceptor事务拦截器 ↓ ① 开启事务从连接池拿连接 → conn.setAutoCommit(false) ↓ ② 执行真实方法 save() ↓ ③ 正常结束 → commit() 抛异常 → rollback()三个核心角色角色职责PlatformTransactionManager事务管理器负责 begin / commit / rollbackTransactionInterceptor事务拦截器AOP 切面的具体执行者Transactional标记注解告诉 Spring 哪些方法需要事务增强PlatformTransactionManager 由 Spring Boot 自动配置创建引入 DataSource spring-tx 依赖即可。2. 事务失效的 6 个高频场景场景 1自调用最常考ServicepublicclassUserService{TransactionalpublicvoidmethodA(){// 操作数据库...this.methodB();// ← this 调用绕过代理对象}TransactionalpublicvoidmethodB(){// 操作数据库...}}this.methodB()调用的是原始对象不是代理对象TransactionInterceptor 没机会拦截。解决① 把 methodB 抽到另一个 Service 类走代理 ② 注入自己Autowired private UserService self; self.methodB();场景 2方法不是 publicTransactionalprivatevoidsave(){// Spring AOP 只拦截 public 方法// 操作数据库}场景 3异常被 catch 吞掉Transactionalpublicvoidsave(){try{// 操作数据库}catch(Exceptione){log.error(出错了,e);// 异常被吞代理以为正常结束走 commit}}代理对象只会在方法抛异常时触发 rollback。catch 吞了异常代理不知道出错了。解决catch 里重新 throw或手动标记回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();场景 4异常类型不匹配Transactional// 默认 rollbackFor RuntimeExceptionpublicvoidsave()throwsIOException{thrownewIOException(文件错误);// 检查异常不回滚}默认只对 RuntimeException 和 Error 回滚。IOException、SQLException 等检查异常不会触发回滚。解决Transactional(rollbackFor Exception.class)场景 5类没交给 Spring 管理publicclassUserService{// 没有 ServiceTransactionalpublicvoidsave(){// 自己 new 出来的对象没有代理对象}}场景 6传播级别不对Transactional(propagationPropagation.NOT_SUPPORTED)publicvoidsave(){// 明确说了不支持事务}3. 传播级别 vs 隔离级别面试容易混的两个概念传播级别 propagation这个方法和已有事务是什么关系 REQUIRED默认有事务就加入没有就新建 REQUIRES_NEW挂起已有事务新建独立事务 NOT_SUPPORTED明确不要事务 SUPPORTS有事务就加入没有就非事务执行 MANDATORY必须在事务中调用否则抛异常 NEVER必须不在事务中调用否则抛异常 NESTED嵌套事务子事务回滚不影响父事务 隔离级别 isolation事务之间互相隔离的程度 READ_UNCOMMITTED → 脏读 READ_COMMITTED → 不可重复读MySQL 默认 REPEATABLE_READ → 幻读InnoDB 默认 SERIALIZABLE → 串行化传播级别影响事务是否生效隔离级别影响并发读写是否安全。不要混。4. Transactional 常用属性Transactional(propagationPropagation.REQUIRED,// 传播级别isolationIsolation.DEFAULT,// 隔离级别用数据库默认timeout30,// 超时秒数readOnlyfalse,// 是否只读rollbackForException.class,// 指定回滚异常类型noRollbackForNullPointerException.class// 指定不回滚异常类型)面试回答模板Transactional 底层基于 AOP 动态代理。调用带 Transactional 注解的方法时 实际执行的是代理对象代理对象通过 TransactionInterceptor 在方法前开启事务 方法正常结束 commit抛异常 rollback。 事务失效最常见的是自调用同一个类里 this.method() 绕过了代理对象 TransactionInterceptor 没机会拦截事务不生效。 其他常见场景包括方法不是 public、异常被 catch 吞掉、 异常类型不匹配默认只回滚 RuntimeException、 类没交给 Spring 管理、传播级别设为 NOT_SUPPORTED。 这里要注意传播级别和隔离级别是两个不同概念 传播级别控制方法和已有事务的关系隔离级别控制并发读写的隔离程度。参考来源Day11-Spring 与 SpringBoot01_Raw【原始素材】/面试资料-B站博主/Spring篇13题 - 飞书云文档.md