Java 面试题

Spring事务传播机制:REQUIRED、REQUIRES_NEW和常见的坑

"讲一下Spring的事务传播机制和@Transactional常见坑。"

为什么面试官会问这道题

事务配错是生产数据一致性bug的主要来源。面试官用这题区分"会用注解"和"真的懂代理、回滚规则、自调用怎么玩"的人。

如何回答

  1. 1

    七种传播模式,重点三个:REQUIRED(默认)有事务就加入没事务就新建;REQUIRES_NEW挂起外层起一个全新事务;NESTED在外层事务里加savepoint。

  2. 2

    默认回滚规则:Spring只在RuntimeException和Error时回滚,不包括checked exception。抛checked异常想回滚要配rollbackFor = Exception.class。

  3. 3

    自调用坑:this.methodB()绕过代理,内层@Transactional完全不生效。

  4. 4

    私有方法坑:Spring AOP代理不到私有方法,@Transactional在private上被静默忽略。

  5. 5

    异常被catch坑:@Transactional方法里try/catch异常又不rethrow,Spring看不到异常正常提交,数据就脏了。

参考回答示例

Spring @Transactional有七种传播模式,真正重要的是三个。REQUIRED是默认——有事务加入,没事务新建。一个REQUIRED方法调另一个REQUIRED方法复用同一个事务,内层回滚整个一起滚。REQUIRES_NEW挂起外层事务起一个全新的内层事务;内层回滚只影响内层,外层可以独立提交。适合审计日志、outbox这种"业务失败也必须落库"的场景。NESTED在外层事务里加savepoint——内层回滚回到savepoint不影响外层,但外层回滚会把内层也滚掉。四个常见坑。第一默认回滚规则只处理RuntimeException和Error。抛checked IOException的话Spring会正常提交事务。解法:@Transactional(rollbackFor = Exception.class)。第二自调用——同类里methodA()写this.methodB(),调用绕过Spring代理,methodB上的@Transactional完全不触发。解法:把bean注入自己、用AopContext.currentProxy()、或重构分到不同bean。第三私有方法——Spring AOP基于CGLIB子类或JDK接口,都代理不到private方法,@Transactional在private上静默失效。第四异常被catch——代码里try/catch住DB异常不rethrow,Spring看不到异常就正常提交,数据脏了。解法是rethrow或显式TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。生产里我把@Transactional当"标记需要事务——再验证代理路径、异常会抛出、没有自调用"。

实用技巧

  • 四个坑必须讲——是实战经验的差异化信号。

  • "checked异常默认不回滚"是code review里最常见的bug。

  • REQUIRES_NEW对审计日志、通知这类场景的用法要能讲。

  • 忘了rollbackFor,即答侠可以实时提示注解参数。

常见问题

什么时候用REQUIRES_NEW?

内层工作必须和外层独立成败的场景——典型如审计日志、outbox、发通知。

NESTED和REQUIRES_NEW有什么区别?

NESTED共用外层连接加savepoint——内层滚不影响外层但外层滚内层跟着滚。REQUIRES_NEW是真的独立物理事务——完全独立。

@Transactional跨线程有效吗?

无效。Spring事务通过ThreadLocal绑线程,任务丢给新线程(executor、@Async)就丢了事务上下文。

面试时担心忘词?即答侠实时助你

即答侠 AI 实时监听面试对话,自动识别问题并即时生成回答建议——无感辅助,让你从容应对每一道题。

免费试用即答侠