@Transactional 标了却没开事务?先看这次调用有没有经过代理

发布时间:2026/7/23 2:53:09
@Transactional 标了却没开事务?先看这次调用有没有经过代理 我见过不少项目把这个问题归因成「注解没扫到」。但如果注解明明在、Bean 也正常先别急着查配置。多数时候是调用没经过 Spring 的事务代理。同一个类里这么调saveOrder不会被拦截Service public class OrderService { public void createOrder() { saveOrder(); } Transactional public void saveOrder() { orderRepository.save(new Order()); } }请求进来时外层确实会先经过OrderService的代理。可createOrder()运行起来以后调用saveOrder()用的是当前对象的this。它没有再绕回代理所以事务拦截器根本看不见这次调用。这也是为什么调试时常会觉得很别扭Transactional就贴在方法上断点也进来了实际却没有事务。Spring Framework 的事务文档把这个行为说得很直白默认代理模式只拦截从代理进入的外部调用同一对象内部的自调用不会触发事务。业务入口和事务方法拆开调用路径就清楚了我更常用的写法是把编排和落库拆成两个 BeanService RequiredArgsConstructor public class OrderFacade { private final OrderService orderService; public void createOrder() { orderService.saveOrder(); } } Service public class OrderService { Transactional public void saveOrder() { orderRepository.save(new Order()); inventoryRepository.decrease(); } }这里OrderFacade注入的是OrderService的代理调用saveOrder()时才会进入事务边界。如果事务本来就该覆盖整个createOrder()那就直接把注解放在入口方法上内部再调私有的辅助方法。别为了「让注解生效」专门搞自注入调用链会越来越难看。事务开了也不等于所有异常都会回滚还有一个面试里很容易接着问的点Transactional public void saveOrder() throws IOException { orderRepository.save(new Order()); throw new IOException(network error); }默认规则下RuntimeException和Error会触发回滚受检异常不会。上面这段如果没有额外配置写入仍可能提交。需要把受检异常也纳入回滚时明确写出来Transactional(rollbackFor IOException.class) public void saveOrder() throws IOException { orderRepository.save(new Order()); throw new IOException(network error); }排查事务问题时我一般先看日志里是不是出现了事务边界再看调用是不是绕过了代理最后才检查传播行为和回滚规则。