网站首页 > 教程文章 正文
凌晨2点,线上交易系统突然出现资金漏洞——数据不一致但事务竟未回滚。这是Spring开发者最不愿面对的噩梦。本文从源码层面剖析7个高频致命陷阱,直击事务失效的底层真相,每个案例均附带可落地的解决方案。建议收藏,关键时刻能救火!
一、非public方法使用@Transactional(字节码级陷阱)
Spring事务基于AOP动态代理实现,而CGLIB代理无法拦截private/protected方法。源码中
AbstractFallbackTransactionAttributeSource的
computeTransactionAttribute方法直接跳过非public方法:
// TransactionAttributeSource类关键判断
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null; // 直接返回不处理
}
解决方案:将事务方法改为public,或改用AspectJ编译时增强。
二、自调用导致代理失效(动态代理机制盲区)
当类内部方法A调用带@Transactional的方法B时,绕过代理对象直接调用,导致事务失效。源码可见JdkDynamicAopProxy通过反射调用时,外部调用才会触发拦截:
// JdkDynamicAopProxy.invoke()
if (method.getDeclaringClass() == Proxy.class) {
// 自调用直接执行原始方法
return method.invoke(proxy, args);
}
解决方案:
- 通过ApplicationContext获取代理对象调用
- 使用AopContext.currentProxy()
- 重构代码结构
三、异常类型未正确抛出(Rollback规则陷阱)
默认仅对RuntimeException和Error回滚。若捕获异常未重新抛出,或抛出checked异常,事务将提交。源码中
RuleBasedTransactionAttribute的rollbackOn方法决定回滚逻辑:
// 默认回滚规则
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
解决方案:
- 手动设置@Transactional(rollbackFor=Exception.class)
- 避免在catch块中"吞掉"异常
四、数据库引擎不支持事务(MyISAM致命陷阱)
使用MyISAM引擎的表完全不支持事务!查看源码DataSourceUtils准备连接时,会通过DatabaseMetaData获取事务支持状态:
// 判断数据库是否支持事务
if (databaseMetaData.supportsTransactions()) {
// 开启事务逻辑
}
解决方案:将所有表引擎改为InnoDB,执行ALTER TABLE table_name ENGINE=InnoDB;
五、传播机制使用错误(嵌套事务黑洞)
PROPAGATION_SUPPORTS等机制在嵌套调用时可能失效。源码中
AbstractPlatformTransactionManager处理传播行为时,会根据存在状态决定是否创建新事务:
// 处理PROPAGATION_REQUIRES_NEW
if (isExistingTransaction(transaction)) {
suspend(transaction); // 挂起当前事务
try {
// 创建新事务
} finally {
resume(transaction);
}
}
典型错误场景:
- REQUIRED方法调用REQUIRES_NEW未通过代理
- NESTED在MySQL中实际使用保存点
解决方案:用代码结构明确事务边界,慎用复杂传播机制。
六、多线程环境下事务分离(连接池地狱)
当使用@Async或线程池时,事务上下文无法跨线程传递。源码中
TransactionSynchronizationManager使用ThreadLocal存储资源:
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
解决方案:
- 使用编程式事务管理
- 传递必要参数到子线程
- 使用TransactionTemplate
七、注解被错误覆盖(继承链危机)
当子类重写父类@Transactional方法时,注解可能失效。源码中
SpringTransactionAnnotationParser优先读取接口/类的注解:
// 注解查找顺序
methodAnnotation = AnnotationUtils.findAnnotation(method, Transactional.class);
classAnnotation = AnnotationUtils.findAnnotation(targetClass, Transactional.class);
解决方案:
- 在子类方法显式添加注解
- 使用接口声明事务方法
- 设置@Transactional(proxyTargetClass=true)
终极排查工具包
- 开启debug日志:logging.level.org.springframework.jdbc=DEBUG
- 检查事务管理器是否注入:PlatformTransactionManager
- 使用TransactionTemplate手动测试
- 断点跟踪TransactionInterceptor.invoke()
记住:事务失效的本质都是代理未生效或资源未绑定。掌握这7个场景,您已具备源码级排障能力。转发本文到技术群,下次救火时您就是英雄!
猜你喜欢
- 2025-05-11 阿里开源MySQL中间件Canal快速入门
- 2025-05-11 MyBatis插件开发实战:手写一个分页插件
- 2025-05-11 Flask数据库——SQLAlchemy
- 2025-05-11 MySQL 到 Hazelcast Cloud 实时数据同步实操分享
- 2025-05-11 sqlmap 详解
- 2025-05-11 一篇文章让你学会Elasticsearch中的查询
- 2025-05-11 Mysql性能优化这5点你知道吗?简单却容易被初学者忽略!
- 2025-05-11 Spring Boot 实现 MySQL 读写分离技术
- 2025-05-11 MySQL利用int类型高性能实现签到活动
- 2025-05-11 MySQL 开发规范
- 05-11阿里开源MySQL中间件Canal快速入门
- 05-11MyBatis插件开发实战:手写一个分页插件
- 05-11Flask数据库——SQLAlchemy
- 05-11MySQL 到 Hazelcast Cloud 实时数据同步实操分享
- 05-11sqlmap 详解
- 05-11一篇文章让你学会Elasticsearch中的查询
- 05-11Mysql性能优化这5点你知道吗?简单却容易被初学者忽略!
- 05-11Spring Boot 实现 MySQL 读写分离技术
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- md5 sha1 (32)
- mybatis plus page (35)
- semaphore 使用详解 (32)
- update from 语句 (32)
- vue @scroll (38)
- 堆栈区别 (33)
- 在线子域名爆破 (32)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)