Spring事务失效在哪些复杂场景下会频繁发生?
摘要:Spring 事务失效是日常开发中非常常见的问题,核心原因是 Spring 声明式事务的实现依赖 AOP 动态代理,一旦代理机制被破坏或不满足事务触发条件,事务就会失效。下面我会逐一讲解最常见的失效场景、原因和解决方案,内容由浅入深,方便你
Spring 事务失效是日常开发中非常常见的问题,核心原因是 Spring 声明式事务的实现依赖 AOP 动态代理,一旦代理机制被破坏或不满足事务触发条件,事务就会失效。下面我会逐一讲解最常见的失效场景、原因和解决方案,内容由浅入深,方便你理解和排查。
一、常见的事务失效场景及解决方案
1. 方法不是 public 修饰
原因:Spring 事务管理器(TransactionInterceptor)只会拦截 public 方法,这是因为 Spring AOP 基于 JDK 动态代理时,只能代理接口的 public 方法;即使是 CGLIB 代理,Spring 也做了限制(源码中会检查方法修饰符)。
示例(失效代码):
@Service
public class UserService {
@Transactional
private void addUser(String username) { // private 方法,事务失效
// 数据库操作
}
}
解决方案:将方法改为 public 修饰:
@Service
public class UserService {
@Transactional
public void addUser(String username) { // 改为 public
// 数据库操作
}
}
2. 同一个类中方法内部调用
原因:Spring 事务通过代理对象实现,若在同一个类中,普通方法调用加了 @Transactional 的方法,本质是直接调用目标对象的方法,而非代理对象,AOP 无法拦截,事务失效。
示例(失效代码):
@Service
public class UserService {
// 普通方法
public void saveUser(String username) {
// 内部调用事务方法,事务失效
addUser(username);
}
@Transactional
public void addUser(String username) {
// 数据库操作
}
}
解决方案:
方案1(推荐):将事务方法抽取到另一个 Service 类,通过依赖注入调用(走代理);
方案2:自注入代理对象,通过代理对象调用(注意循环依赖问题):
@Service
public class UserService {
// 自注入代理对象(Spring 4.3+ 支持,或通过 ApplicationContext 获取)
@Autowired
private UserService userService;
public void saveUser(String username) {
// 通过代理对象调用,事务生效
userService.addUser(username);
}
@Transactional
public void addUser(String username) {
// 数据库操作
}
}
3. 事务注解属性配置错误
原因:@Transactional 的属性配置不符合业务场景,导致事务不生效或回滚异常。
常见错误配置:
propagation = Propagation.NOT_SUPPORTED(不支持事务,会挂起当前事务);
propagation = Propagation.SUPPORTS(仅当有事务时才生效,无事务则不使用事务);
rollbackFor 未配置,默认只回滚 RuntimeException 和 Error,检查异常(如 SQLException)不会触发回滚。
