当前位置: 首页 > news >正文

深入浅出 Spring AOP:@Transactional 事务失效的三大致命陷阱

深入浅出 Spring AOP:@Transactional 事务失效的三大致命陷阱

欢迎来到 Spring AOP 宇宙中最深、也是埋葬了最多程序员的“万人坑”——@Transactional事务失效。
在企业级开发中,@Transactional绝对是出场率最高的 AOP 注解。但正因为大家用得太顺手了,往往忽略了它底层的代理机制,导致写出了“以为能回滚,其实早提交了”的致命 Bug。
之前注意到很多开发者的代码里写了@Transactional(rollbackFor = Exception.class),这说明已经避开了第一个低级坑(默认只回滚 RuntimeException)。但这仅仅是开始。
接下来,我们用“架构师的大白话 + 灾难推演”,直接撕开@Transactional最致命的 3 个连环坑。

一、 致命大坑一:“祸起萧墙”的自调用失效

这是全宇宙最容易踩的坑,80% 的新手都在这上面摔过跟头。
【案发现场】

@ServicepublicclassOrderService{// 方法 A:没有事务publicvoidcreateOrder(Orderorder){// ... 一些准备工作// 直接调用同类中的方法 Bthis.saveOrderAndPay(order);}// 方法 B:加了事务!@Transactional(rollbackFor=Exception.class)publicvoidsaveOrderAndPay(Orderorder){orderMapper.insert(order);inti=1/0;// 模拟灾难:突然报错paymentMapper.insert(payment);}}

【灾难推演】
外面调用createOrder,然后它调用了同类里面的saveOrderAndPay。走到1 / 0的时候报错了。你以为订单会被回滚?错!订单被死死地插进了数据库,事务根本没有生效!
【架构原理解析:秘书与老板】
AOP 的底层原理是动态代理。Spring 会给你的OrderService创建一个“代理对象”(我们叫它秘书)。
当外部类调用你的方法时,是先找到秘书,秘书开启事务,然后再找老板(你的真实类)。
但是!在createOrder方法里,你用的是this.saveOrderAndPay()this代表的是老板本人!老板自己跟自己说话,根本不会经过门外的秘书。既然没经过秘书(代理对象),那@Transactional设下的 AOP 拦截就完全没触发,事务压根就没开启。
【架构级解法】
核心思想是**“强行找秘书办”**。
解法 1(推荐):自己注入自己
Spring 支持循环依赖注入自身。

@ServicepublicclassOrderService{@AutowiredprivateOrderServiceself;// 注入代理对象(秘书)publicvoidcreateOrder(Orderorder){self.saveOrderAndPay(order);// 找秘书去调方法 B,事务生效!}}

解法 2:借助 AOP 上下文获取代理对象

((OrderService)AopContext.currentProxy()).saveOrderAndPay(order);

二、 致命大坑二:“庸医害人”的异常吞咽

我们在写 Redis 缓存时,大量使用了try-catch来做 Fail-Open。但在事务里,乱用try-catch是毁灭性的。
【案发现场】

@Transactional(rollbackFor=Exception.class)publicvoidupdateShopData(Shopshop){try{shopMapper.update(shop);// ... 调用其他微服务,突然超时报错remoteService.call();}catch(Exceptione){// 救援队来了:我把它 catch 住,打个日志吧!log.error("更新失败了",e);}}

【灾难推演】
代码执行报错了,但数据库的update操作没有回滚,被提交了!
【架构原理解析:烽火台失联】
@Transactional切面(也就是秘书)是怎么知道要回滚的?它是靠捕获你方法抛出的异常(烽火台的狼烟)。
如果你的业务代码里try-catch抓住了异常,并且没有重新抛出去,就等于你把狼烟给掐灭了。秘书一看,方法顺顺利利执行完了,没有抛出任何异常,于是大手一挥:“大家辛苦了,提交事务!”
【架构级解法】
解法 1:如果 catch 了,一定要重新扔出去
让切面感知到异常。

}catch(Exceptione){log.error("更新失败",e);thrownewBizException("业务执行失败",e);// 扔个自定义异常出去}

解法 2:手动命令秘书回滚
如果不想抛异常(比如要返回一个友好的 JSON 结果),可以手动打标记。

}catch(Exceptione){log.error("更新失败",e);// 手动打回滚标记TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();returnResult.fail("更新失败");}

三、 致命大坑三:“闲人免进”的非 public 方法

【案发现场】

@Transactional(rollbackFor=Exception.class)protectedvoiddoUpdate(Shopshop){// 注意:这里写了 protected 或者是 privateshopMapper.update(shop);inti=1/0;// 报错}

【灾难推演】
发生异常,事务不回滚!
【架构原理解析:代理机制的盲区】
无论是 JDK 动态代理(基于接口),还是 CGLIB 动态代理(基于继承子类),它们在生成代理类时,默认只拦截public方法。
如果你把@Transactional加在privateprotected或包级私有方法上,Spring AOP 在启动扫描时会直接无视它(甚至都不会报错,就是单纯的静默失效)。
【架构级解法】
永远、永远、永远只把@Transactional加在public方法上。

总结:架构师的终极心法

了解了这三大天坑,再看 Spring 的 AOP 和事务,会发现它们不再是神秘的黑魔法,而是有着严格物理规律的机器部件。
以后无论再遇到什么稀奇古怪的 AOP 或者事务失效问题,只要在脑子里默念这三句话,一切 Bug 都将无所遁形:

  1. AOP 就是一个中间人(代理)。
  2. 不走中间人(自调用),不生效。
  3. 中间人收不到报错信号(被 Catch),不回滚。
    把这几条物理规律刻在脑子里,排查线上的脏数据问题时,只需扫一眼代码,就能一针见血地指出问题所在。
http://www.jsqmd.com/news/558735/

相关文章:

  • 【紧急预警】FastAPI 2.0升级后AI流式中断率飙升47%?我们逆向分析了32个生产环境trace,定位async_generator内存泄漏根因
  • DLSS Swapper:让显卡玩家轻松匹配最佳DLSS版本的智能管理工具
  • 一键部署MedGemma:打造个人医学AI研究环境
  • 卷积神经网络(CNN)与BERT特征融合:面向视觉文档的文本分割
  • 新手友好!Anything to RealCharacters 2.5D转真人引擎界面操作详解
  • 别再只盯着Loss曲线了!TensorBoard的SCALARS面板还有这些隐藏玩法(附GAN训练实战)
  • AIGlasses_for_navigation效果展示:雨天/阴影/反光环境下盲道分割稳定性案例
  • 基于python框架的大学生创新创业项目管理系统vue
  • HexView脚本进阶:巧用/CR参数实现多区域数据‘挖空’,为自动化测试铺路
  • 基于ChatGLM-6B的智能写作助手开发实战
  • YOLOFuse新手入门:3步完成双流目标检测模型部署
  • 扶摇速记:第一性原理记单词-回归、坍塌、本质、极简、融通
  • 从“偏科生”GPT-3到“全能选手”:聊聊MMLU基准如何推动大模型进化
  • 高效解析网盘直链:突破下载限制的技术实践指南
  • Nunchaku FLUX.1-dev 文生图节点化开发:基于Node.js构建图像生成API服务
  • 2026年知名的太阳能路灯系统/太阳能路灯/四川太阳能路灯/太阳能路灯批发实力厂家如何选 - 品牌宣传支持者
  • 基于python框架的船舶物流运输管理系统设计vue
  • Qwen3-VL-8B功能体验:上传手机碎屏图,看AI如何判断维修与报价
  • 【进阶指南】VSCode + Clang-Format:从零定制你的专属代码风格(130+配置项实战解析)
  • Wan2.2-I2V-A14B在MCP架构中的应用:模块化AI服务设计
  • FUTURE POLICE在微信小程序开发中的应用:实时语音分析功能实现
  • 英雄联盟玩家必备:League Akari如何让你的游戏效率提升300%
  • 2026年质量好的矩阵光电霍尔开关芯片/EG屹晶微电源管理芯片/EG屹晶微PFC/LLC控制器芯片/矩阵光电高灵敏度InSb霍尔元件芯片厂家实力哪家强 - 品牌宣传支持者
  • Go JSON 序列化性能优化
  • 使用Docker快速部署RMBG-1.4服务:环境隔离与性能优化
  • Maxar Open Data:地理空间智能的开源卫星影像平台
  • 2026年靠谱的数控辊轴车床/数控重型轧辊车床/数控轧辊车床/数控轧辊铣床车床供应商怎么选 - 品牌宣传支持者
  • 银行卡密码安全背后的秘密:从PIN到PIN block的完整解析(附代码示例)
  • 别再复制粘贴了!手把手教你从零在Ubuntu 20.04上配置Intel RealSense D435i与ROS Noetic
  • 图文翻译神器translategemma-12b-it:本地部署与使用全攻略