连载中 1/18

Spring 开篇:@Transactional 加了,事务却没生效

2026-11-02 · 66 阅读 · 0 评论 · 0 赞

事故现场:明明加了 @Transactional

订单方法 saveOrder() 内部要同步写审计日志,于是调用了本类的 audit() 方法——audit 上标注了 @Transactional。上线后奇怪的事发生了:audit 抛异常,订单照存,日志表还多了半条脏数据。配置没问题、注解没拼错、依赖都在。真相:类内部的 this 调用没走代理,@Transactional 形同虚设。这是 Spring 世界每天都在发生的事故,也是本系列开篇的最佳标本——它暴露了一个根本认知:在 Spring 里,你拿到的从来不是你写的那个对象。

第一原理:容器里住的是代理

你写的类是一张白纸;交给 Spring 之后,容器返回给你的可能是它的增强版:加事务、加日志、加缓存——全靠 AOP 动态代理在方法前后织入逻辑。增强要生效,调用必须经过代理对象;而类内部用 this 调用,等于绕过代理直捣本体,织入的逻辑全部失联。这不是 bug,是动态代理的工作原理:代理只能拦截「穿过它」的调用。记住这句话,本系列一半的事故分析都有解了。

代码复现

@Service
public class OrderService {

    public void saveOrder() {
        // this.audit():this 是原始对象,不经过代理 → 事务失效
        audit();
    }

    @Transactional
    public void audit() {
        System.out.println("当前是否存在事务:"
                + TransactionSynchronizationManager.isActualTransactionActive());
    }
}

外部直接调 audit()(比如 Controller 里 orderService.audit()),输出 true;saveOrder() 里 this 调用,输出 false——同一行代码,两种命运,只差「调用是否经过代理」。修法三条:注入自身代理再调(@Lazy 自注入)、把 audit 拆到另一个类、或开启 exposeProxy 后用 AopContext.currentProxy()。细节留给事务失效篇(第 9 篇)展开,今天先立认知。

这个系列的路线图

十八篇分五段:IoC 与 Bean 生命周期(九步旅程、三级缓存与循环依赖、BeanPostProcessor、FactoryBean);AOP 双篇(JDK 动态代理与 CGLIB、切面织入);事务三篇(实现原理、失效八场景、传播行为七式);注入与事件(@Autowired 与 @Resource、循环依赖进阶、事件机制);启动与装配(Spring Boot 启动流程、自动装配、自定义 Starter、常见扩展点)。收官放一张 Spring 作战地图。

准备工作

JDK 17 + Spring Boot 3.x,每个知识点配最小可跑 Demo。两个好用的观察工具:TransactionSynchronizationManager.isActualTransactionActive() 判断当前线程事务是否激活,比翻日志直观;AopUtils.isAopProxy(bean) 一行代码看 Bean 是真身还是代理。

下一篇从容器的心脏讲起:getBean 的背后,一个 Bean 从实例化到销毁的完整旅程——九步流水线,步步有角色。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Spring#Spring Boot#事务失效#代理#系列开篇

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10136 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9900 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9330 阅读 · 21 评论 · 287 赞