Transactional 注解与 AOP
转载 为防止消失 如有侵权请联系 原链接 https://www.dazhuanlan.com/2019/11/30/5de14b06398b8/
当我们在一个 private 方法上打上 @Transactional 注解,IDEA 会提示 Methods annotated with '@Transactional' must be overridable
。比如下面的例子:
1
|
@Service
|
有时候我们想让事务的范围尽可能的小,可能就会写出这样的代码,在 method1 先做一些非事务的事情,然后在 method2 中做事务相关的事情。此时,如果我们用 IDEA 的话,应该就会出现上面的提示信息,eclipse 我没用不清楚会不会有这样的其实。为什么被 @Transactional 注解的方法必须是可重写的呢?
如果我们忽略掉这个信息,你会发现编译运行是没有问题的。在外部调用 UserService.method1() 时,如果 method2 没有发生异常的话,你会发现一切都是正常的。为什么 IDEA 要这样提示呢,真是怪了!
但是,当 method2 发生异常时,你会惊奇的发现 method2 的事务并没有回滚!
AOP
要理解事务为什么没有回滚,我们就要回顾一下 AOP 的知识。在 Spring 中默认是通过 JDK 动态代理的方式来实现 AOP 的,对于打了 @Transactional 注解的类,Spring 动态代理会生成一个代理 bean 和一个真实的目标 bean。
我们回头看看 method1 并没有打上注解,所以 method1 并不会被事务切面环绕。而 method2 是通过 method1 调用的,隐藏的调用对象是真实的目标 bean,真实的目标 bean 是没有切面逻辑的,切面逻辑都在代理 bean 上。这就是为什么事务没有回滚的原因,如下图所示:
按照这样的理解,只要我们在 method1 方法上打上 @Transactional 注解,事务就能生效了,method2 的注解是多余的。此时,我们应该就能理解 IDEA 的善意提示了。因为 UserService 没有接口,所以只能通过 CGLIB 的方式来实现动态代理,而 CGLIB 是通过继承的方式来进行代理,需要对目标 bean 的方法进行重写,但是 private 修饰的方法是不能重写的,所以就会出现这样的提示。
但是,在 method1 上打注解,method2 上不打,那不是违背了我们当初让事务范围最小化的出发点了吗?一切又回到了原地。还有没有其他解决方法?
- 把 method1 方法搬到 controller 层,嘿嘿,这个方法看起来很鸡贼,但是好像是更加合理的?
- 如果代理类和目标类合为一个,有注解的方法有切面环绕,没有注解的方法没有切面环绕,这样是不是就行了?AspectJ 静态代理就是这样的一门技术,在编译成 class 字节码的时候在方法周围织入切面逻辑。
总结
不仅仅是 @Transactional 会这样,其他的注解也是同理的,所以我们要深入理解 AOP 的原理,面对这些问题才能游刃有余。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!