Spring 事务失效场景深度解析
Spring 事务失效
一、底层原理
Spring 事务是 AOP 实现的,通过 TransactionInterceptor 包装目标方法,生成代理对象(JDK 动态代理 / CGLIB),方法调用时先经过拦截器,根据事务回滚默认规则,决定是否开启事务、提交或回滚。
方法调用时先经过拦截器,拦截器就是 Spring 为 @Transactional 创建的 TransactionInterceptor。它被织入代理对象的拦截器链里,JDK 动态代理通过 InvocationHandler,CGLIB 通过 MethodInterceptor,调用代理对象方法时会先执行 TransactionInterceptor,然后再执行目标方法。
TransactionInterceptor.invoke() 本身只负责事务逻辑,它位于代理对象的拦截器链中。方法调用时先进入拦截器链,每个拦截器依次调用 invoke(),TransactionInterceptor 内部直接调用 method.invoke(target, args) 是为了执行链中下一个环节或最终目标方法。拦截器链的循环和调用顺序是在代理对象生成阶段就已经确定的。
TransactionInterceptor 核心逻辑
1 | invoke() { |
调用链路图
1 | 调用代理对象.method() |
二、几种常见失效场景
1. 未捕获异常
如果一个事务方法中发生了未捕获的异常,并且异常未被处理或传播到事务边界之外,那么事务会失效,所有的数据库操作会回滚。
2. 非受检异常
默认情况下,Spring 对非受检异常(RuntimeException 或其子类)进行回滚处理,这意味着当事务方法中抛出这些异常时,事务会回滚。
3. 事务传播属性设置不当
如果在多个事务之间存在事务嵌套,且事务传播属性配置不正确,可能导致事务失效。特别是在方法内部调用有 @Transactional 注解的方法时要特别注意。
4. 多数据源的事务管理
如果在使用多数据源时,事务管理没有正确配置或者存在多个 @Transactional 注解时,可能会导致事务失效。
5. 跨方法调用事务问题
如果一个事务方法内部调用另一个方法,而这个被调用的方法没有 @Transactional 注解,这种情况下外层事务可能会失效。
6. 事务在非公开方法中失效
如果 @Transactional 注解标注在私有方法上或者非 public 方法上,事务也会失效。
7. 使用 this 调用是否生效
因为 Spring 事务是通过代理对象来控制的,只有通过代理对象的方法调用才会应用事务管理的相关规则。当使用 this 直接调用时,是绕过了 Spring 的代理机制,因此不会应用事务设置。