







Spring 事务失效是日常开发中高频踩坑点,核心原因是事务注解的生效条件未满足(如代理失效、传播行为不当、异常处理错误等)。以下梳理 10 类典型失效场景,结合原理和解决方案,覆盖开发中 90% 以上的失效情况:
Spring 事务默认基于 动态代理(JDK 动态代理 / CGLIB) 实现,只有通过代理对象调用方法,事务注解(@Transactional)才会生效;且事务管理器(如DataSourceTransactionManager)需正确配置。所有失效场景本质都是 “脱离了代理的管控” 或 “事务规则未满足”。
Spring 事务的代理逻辑(TransactionInterceptor)仅对 public 方法 生效:
AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法会过滤非 public 方法)。@Service
public class UserService {
将事务方法改为 public 修饰(必须)。
Spring 事务通过代理对象实现,同类内直接调用方法(而非代理对象),会绕过代理逻辑,事务注解失效。
@Service
public class UserService {
最优:拆分到不同 Service将事务方法抽离到独立的 Service,通过依赖注入调用(走代理):
@Service
public class UserService {
@Resource
private UserTxService userTxService;
public void outerMethod(Long id) {
userTxService.innerMethod(id);
次优:自注入代理对象在本类中注入自身的代理对象(需开启暴露代理配置):
// 1. 开启暴露代理(application.yml)
spring:
aop:
proxy-target-class: true
expose-proxy: true
兜底:手动编程式事务放弃声明式事务,用TransactionTemplate手动控制:
@Service
public class UserService {
@Resource
private TransactionTemplate transactionTemplate;
public void outerMethod(Long id) {
transactionTemplate.execute(status -> {
innerMethod(id);
return null;
});
}
public void innerMethod(Long id) {
Spring 事务默认只捕获 未处理的 RuntimeException/Error,并在异常抛出时触发回滚;若异常被try-catch捕获且未重新抛出,事务管理器感知不到异常,会执行提交而非回滚。
@Service
public class UserService {
@Transactional
public void updateUser(Long id) {
try {
捕获后重新抛出异常(推荐):
@Transactional
public void updateUser(Long id) {
try {
手动触发回滚(适合需捕获异常的场景):
@Transactional
public void updateUser(Long id) {
try {
Spring 事务默认只对 RuntimeException 和 Error 回滚,若抛出的是 Checked 异常(如IOException、SQLException),且未配置rollbackFor,事务不会回滚。
@Service
public class UserService {
@Transactional
public void updateUser(Long id) throws IOException {
通过rollbackFor指定需要回滚的异常类型:
// 指定回滚所有异常(或精准指定IOException)
@Transactional(rollbackFor = Exception.class)
public void updateUser(Long id) throws IOException {
@Transactional(propagation = ...) 定义了事务的传播规则,若配置为以下值,会导致事务失效:
Propagation.NOT_SUPPORTED:以非事务方式执行,若有当前事务则挂起;Propagation.NEVER:以非事务方式执行,若有当前事务则抛异常;Propagation.SUPPORTS:有事务则加入,无则以非事务执行(无事务时失效)。@Service
public class UserService {
使用正确的传播行为(默认Propagation.REQUIRED即可满足 99% 场景):
// 默认REQUIRED:有事务则加入,无则新建
@Transactional
public void updateUser(Long id) {
Spring 事务需要绑定对应的PlatformTransactionManager(如DataSourceTransactionManager),若未配置,事务注解仅为 “空注解”,不会生效。
// 仅配置数据源,未配置事务管理器
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
手动配置事务管理器(Spring Boot 2.x+ 若只配置一个数据源,会自动配置,多数据源需手动指定):
@Configuration
public class TransactionConfig {
@Resource
private DataSource dataSource;
@Bean
public PlatformTransactionManager transactionManager() {
多数据源场景下,若未通过@Transactional(value = "xxxTransactionManager")指定对应的事务管理器,Spring 无法识别该用哪个,事务失效。
@Service
public class UserService {
指定事务管理器的 bean 名称:
// 假设配置了两个事务管理器:txManager1、txManager2
@Transactional(value = "txManager1")
public void updateUser(Long id) {
Spring 事务依赖数据库的事务支持,若 MySQL 表使用MyISAM引擎(不支持事务),即使配置了 Spring 事务,也无法回滚。
将表引擎改为InnoDB(MySQL 5.5+ 默认 InnoDB):
-- 修改表引擎
ALTER TABLE user ENGINE = InnoDB;
proxy-target-class=true(优先 CGLIB),但手动关闭后可能失效。spring:
aop:
proxy-target-class: true
public interface UserService {
void updateUser(Long id);
}
@Service
public class UserServiceImpl implements UserService {
@Transactional
@Override
public void updateUser(Long id) {
@Transactional(timeout = n) 表示事务超时时间(秒),若业务执行时间超过 timeout,事务会被强制回滚,看似 “失效”(实际是超时回滚)。
@Transactional(timeout = 1)
根据业务实际耗时调整 timeout 值(避免过小):
@Transactional(timeout = 30)
logging:
level:
org.springframework.transaction: DEBUG
org.springframework.jdbc.datasource.DataSourceTransactionManager: DEBUG
Spring 事务失效的核心可归纳为三类:
生产环境中,优先使用声明式事务(@Transactional)+ 避免同类内部调用 + 正确处理异常,复杂场景(如多数据源、手动回滚)可结合编程式事务(TransactionTemplate),既能保证可读性,又能避免失效。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。