@Transactional 为什么能生效?一次 Debug 看懂 Spring 如何偷偷加事务

@Transactional 为什么能生效?一次 Debug 看懂 Spring 如何偷偷加事务 上篇我们聊了Spring 为什么疯狂使用动态代理AOP 为什么不用继承当时评论区有人问“既然 Spring 已经偷偷创建了代理对象那 Transactional 到底什么时候加进去的为什么加个注解事务就有了”这个问题问得很好因为很多人学事务时其实一直有个误解Transactionalpublic void save(){}加个注解事务就自动开启抛异常自动回滚没异常自动提交。时间久了很容易产生一种错觉Transactional 事务但如果真这样后面很多经典问题就解释不通同类调用为什么失效private 为什么失效final 为什么失效所以今天不背八股直接 Debug 看源码。看看 Spring 到底什么时候偷偷加了事务。先写个最简单例子ServicepublicclassUserService{Transactionalpublicvoidsave(){System.out.println(执行业务);inti1/0;}}调用userService.save();执行结果开启事务执行业务异常回滚看起来像这样begin();save();rollback();但问题是这些代码你根本没写。事务从哪来的老规矩先看对象。打断点System.out.println(userService.getClass());输出class com.demo.UserService$$SpringCGLIB$0很多人第一次看到这里会愣一下。我写的是UserService怎么运行时变成UserService$$SpringCGLIB$0这个类名已经在提醒你容器里的对象不是原对象而是代理对象。所以userService.save();真正执行的是proxy.save();不是target.save();这意味着事务并不在你的业务代码里而是在代理逻辑里。继续 Debug。进入CglibAopProxy一路往下DynamicAdvisedInterceptor继续进入intercept()这里会看到一段核心代码ListObjectchainthis.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);第一次看源码基本都会懵但翻译成人话其实很简单获取当前方法需要执行的拦截器链比如一个方法可能同时有日志事务权限缓存最终形成日志↓事务↓目标方法本质就是责任链模式继续点开 chain。会看到一个非常关键的类TransactionInterceptor这次名字已经不藏了事务真正干活的人出现了。继续进去invoke()再进去invokeWithinTransaction()很快能看到createTransactionIfNecessary();创建事务。然后retValinvocation.proceedWithInvocation();执行目标方法save();执行成功commitTransactionAfterReturning();提交事务。执行异常completeTransactionAfterThrowing();回滚事务。整个调用过程就清晰了代理对象↓事务拦截器↓开启事务↓执行目标方法↓提交/回滚看到这里很多人以前的理解就得改一下Transactional 本身不会开启事务它只是一个标记告诉 Spring这个方法需要事务真正执行事务逻辑的是TransactionInterceptor但新的问题来了。Spring 怎么知道这里有Transactional然后决定创建事务代理继续打断点。进入AbstractAutoProxyCreator再进入postProcessAfterInitialization()看到这里上一篇其实埋过一个坑。既然 Spring 能创建代理那它总得有个时机决定这个对象需不需要代理真正干这件事的就是BeanPostProcessor事务、AOP、缓存、异步看起来像完全不同的功能但底层走的是同一套流程实例化 Bean↓初始化 Bean↓BeanPostProcessor↓判断是否需要增强↓创建代理对象↓放入容器所以事务完整过程其实是发现 Transactional↓创建事务增强器↓BeanPostProcessor 创建代理↓调用代理对象↓进入 TransactionInterceptor↓开启事务↓执行方法↓提交/回滚到这里经典面试题就能解释了ServicepublicclassUserService{publicvoida(){b();}Transactionalpublicvoidb(){}}很多人都遇到过事务失效网上标准答案因为同类调用。但这句话其实没解释本质。真正执行的是a()↓this.b()注意这里this对象内部直接调用绕过了代理相当于目标对象↓直接执行而不是代理对象↓TransactionInterceptor事务拦截器根本没执行所以事务自然没了。本质不是事务失效而是没走代理。这也是为什么 Spring 学到后面你会发现很多问题最后都会绕回代理。动态代理解决增强。AOP 基于代理实现。事务又建立在 AOP 之上。整个链路终于串起来了动态代理↓AOP↓事务拦截器↓Transactional最后留个问题下面两种写法事务还能生效吗Transactionalprivatevoidsave(){}Transactionalfinalvoidsave(){}为什么评论区猜猜下一篇继续 DebugTransactional 为什么会失效一次 Debug 看懂 Spring 踩过的所有坑