Spring事务的1道面试题

news/2024/10/10 13:42:27/文章来源:https://www.cnblogs.com/mangod/p/18456140

每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。

原理

Spring事务的原理是:通过AOP切面的方式实现的,也就是通过代理模式去实现事务增强。

具体过程是:对包含@Transactional注解的方法进行拦截,然后重写,重新在方法里加入异常回滚的逻辑。而且,每个线程都是独立管理自己的事务,相互隔离。

原理简单,使用起来也简单,也就是在方法上打上@Transactional注解,然后事务就正常生效了。也很少有人去验证异常情况下是否能真正的回滚。

Spring事务让我熟悉的地方是哪哪看起来都简单,让我陌生的地方使用时的变种较多,有时候莫名其妙的不生效。

源码

以上原理的相关源码如下:

实践出真知

但是 [半支烟] 偶尔会在编码过程中发现有些场景下的事务是失效的,总有些情况让你想不到,总有一些坑点等你去跳。

[半支烟] 觉得验证事务的最好方式就是:记住基本原则 + 动手实践。记住基本原则可以快速处理常规问题,动手实践可以验证偏门问题或者不确定的问题。

几种事务不生效的用法

如下是常见的几种Spring事务不生效的用法,有空的读者一定要牢记,对日常编码很有帮助,同时面试时也能说几句。

  • private方法

Spring是通过AOP代理的方式实现事务增强的,但是private方法无法被代理,所以在private方法上打@Transactional注解是不生效的。

  • final、static修饰的方法

和private方法类似,final和static修饰的方法也无法被代理,所以@Transactional注解也不生效。

因为,static是属于类方法,final修饰的方法无法被重写,自然也就无法植入事务增强代码。

  • Bean对象没有被Spring托管

某个类一定要被Spring托管,那才能通过@Transactional注解去增强事务。如果只有@Transactional注解,而没有把类交给Spring托管,事务也是不生效的。类似如下情况:

// 此处没有@Service注解,此类不被spring托管,及时有@Transactional也不生效
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic final void createAndUpdateUser() {createUser();updateUserById();}public void createUser() {User user = new User();user.setId(2L);user.setName("test2");user.setEmail("test2" + "@test.com");userMapper.insert(user);System.out.println("create user");}public void updateUserById() {User user = userMapper.findById(1L);user.setName("admin1");userMapper.update(user);int i = 1 / 0; // 此处会抛出异常System.out.println("update user");}
}
  • 异常被吞掉

如果在业务代码里,通过try......catch捕获了异常,同时又没有继续抛出异常时,Spring事务也是不生效的。

因为代理增强的逻辑就是要发现了异常,才能回滚事务。如果异常被方法本身吞掉了,则代理会认为没有异常,从而无法回滚。

  • 非RuntimeException异常

Spring事务默认会回滚RuntimeException 及其子类,以及 Error 类型的异常。如果是其余异常,则不会回滚。源码处可见:

这种非RuntimeException异常场景下,需要做2个动作从而保证事务回滚。

  1. 捕获异常,然后抛出自定义异常。
  2. 自行在@Transactional注解中增加@Transactional(rollbackFor = XxxxxxxException.class)属性。或者直接使用rollbackFor = Exception.class,也就免去了第一步。
  • 异步线程的场景

多个线程的场景下,只需要牢记每个线程只管理自己的事务即可。每个线程都有一个独立的事务上下文,存在ThreadLocal中,所以事务信息在不同线程之间是隔离的。

  • 重灾区:在同一个类中调用本类的方法

这个失效场景,是最容易出错的,而且变种还多。在同一个类中调用本类的方法时,牢记以下2点,即可破局:

  1. 是否会开启事务依赖此类的第一个被外部调用的方法。如果此类的第一个被外部调用的方法有@Transactional注解,那事务生效。
  2. 调用自己内部方法时,采用的是this.xxxMethod()的方式,这种方式是不会走AOP代理的,所以被调用的内部方法的@Transactional注解不生效。

如果确实需要调用内部方法,并且要事务生效的话,那只能将被调用的内部方法独立到新的类中,同时交给Spring管理。

一道面试题

以上关于事务不生效的用法都比较好记,只有在同一个类中调用本类的方法场景下存在多种变种。具体请看这道面试题。请问以下createAndUpdateUser方法的事务生效吗?

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic final void createAndUpdateUser() { //注意这里有final修饰createUser();updateUserById();}@Transactionalpublic void createUser() {User user = new User();user.setId(2L);user.setName("test2");user.setEmail("test2" + "@test.com");userMapper.insert(user);System.out.println("create user");}@Transactional(rollbackFor = Exception.class)public void updateUserById() {User user = userMapper.findById(1L);user.setName("admin1");userMapper.update(user);int i = 1 / 0; // 此处会抛出异常System.out.println("update user");}
}

如果按照重灾区:在同一个类中调用本类的方法里提到的2个原则,则事务全部生效。

如果按照final、static修饰的方法里提到的原则,则事务全部不生效。

那结果如何呢?结果是以上方法的事务全部生效。

为什么呢?这里在补充一个原则:final修饰的方法如果带上@Transactional注解,事务情况按照被调用的方法自身的事务托管情况而定。

因为以上代码中的createUser方法和updateUserById方法,都有@Transactional注解,所以都生效。

这种特殊情况也实在是让人瞠目,不过只需要牢记以上几种不生效的用法即可,谁没事儿写这种@Transactional + final的代码呢?除了面试会问......

总结

本篇主要聊了几种事务不生效的用户,有兴趣的读者可以记一下。同时,还出了一道特殊场景的面试题,供读者自行实践。希望对你有帮助!

本篇完结!欢迎 关注、加V(yclxiao)交流、全网可搜(程序员半支烟)

原文链接:https://mp.weixin.qq.com/s/V5KpVk0kDhc9vWctOy7X9A

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/811051.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装 Anaconda、PyTorch(GPU 版)库与 PyCharm

Anaconda 是一款巨大的 Python 环境集成平台,里面包含了 Python 解释器、Jupyter Notebook 代码编辑器以及很多的第三方库,所以安装 Anaconda 后我们无需再安装 Python 解释器,非常方便。 一、安装 Anaconda 1.卸载 Anaconda(可选) 如果我们原来的电脑上安装过 Anaconda,…

Ubuntu nginx 安装

1. 下载源码 下载页面:https://nginx.org/en/download.html 下载地址:https://nginx.org/download/nginx-1.27.2.tar.gz curl -O https://nginx.org/download/nginx-1.27.2.tar.gz 2. 依赖配置 sudo apt install gcc make libpcre3-dev zlib1g-dev openssl libssl-dev 3. 编译…

ArgoWorkflow教程(六)---无缝实现步骤间参数传递

之前我们分析了,Workflow、WorkflowTemplate 、template 3 者之间如何传递参数。 本文主要分析同一个 Workflow 中的不同 step 之间实现参数传递,比如将上一个步骤的输出作为下一个步骤的结果进行使用(而非以文件方式传递)。1. 概述 然后就是之前只分析了 Workflow、Workfl…

OOOPS:零样本实现360度开放全景分割,已开源 | ECCV24

全景图像捕捉360的视场(FoV),包含了对场景理解至关重要的全向空间信息。然而,获取足够的训练用密集标注全景图不仅成本高昂,而且在封闭词汇设置下训练模型时也受到应用限制。为了解决这个问题,论文定义了一个新任务,称为开放全景分割(Open Panoramic Segmentation,OPS…

如何将React项目,部署到Web服务器的Tomcat 上

将 React 应用部署到 Tomcat 服务器上通常需要将其构建为静态文件,然后将这些文件放入 Tomcat 的 webapps 目录。以下是具体步骤: 步骤指南 1. 构建 React 应用 首先,你需要在本地构建你的 React 应用。npm run build这会在项目根目录下生成一个 build 文件夹,里面包含了优…

实时显示和拍照存储,IFD-x让你实时掌握温度信息 非接触式热成像仪器红外线成像

实时显示和拍照存储,IFD-x让你实时掌握温度信息 非接触式热成像仪器红外线成像非接触式热成像仪器,IFD-x采用红外阵列高精度温度传感器和先进的软件算法。它能够对视场范围内的任何物体进行红外成像,成像分辨率达到512*384像素,温度灵敏度为0.1℃,绝对精度为1.5℃,刷新频…

USB 鼠标的实现

目录前言5 USB 标准请求5.1 USB 标准设备请求的结构5.2 GET_DESCRIPTOR 请求5.3 SET_ADDRESS 请求5.4 SET_CONFIGURATION 请求6 设备描述符的实现9 配置描述符集合的结构9.1 配置描述符结构9.2 接口描述符的结构9.3 端点描述符的结构9.4 HID 描述符的结构9.5 [示例:描述符]11 …

USB 概述及协议基础

《圈圈教你学 USB》第 1 章学习笔记1 USB 是什么?1)USB(Universal Serial Bus,通用串行总线)2 USB 的特点1)USB 协议版本与速度:来源:https://www.usbzh.com/article/detail-199.html2)开发 USB 常用网站: https://www.usb.org3 USB 的拓扑结构USB 是主从结构,主机为…

AI云平台介绍

AI云平台是基于云计算基础设施,集成机器学习、深度学习、自然语言处理、计算机视觉等先进AI技术,通过云端服务的形式,向用户提供数据预处理、模型训练、模型部署、预测推理等一系列AI开发与应用服务的平台。AI云平台是基于云计算基础设施,集成机器学习、深度学习、自然语言…

无人直播自动化回复客户咨询

我们插件是根据页面元素变动进行自动化操作的,想要实现网页版自动化,必须了解html以及dom结构,还有xpath定位方法。 各大直播后台页面结构不一样,所以要进行兼容处理,我们一个插件支持以下直播或客服平台 唯一客服浏览器插件 十年开发经验程序员,离职全心创业中,历时三年…

AMD 线程撕裂者7970X 搭配双卡A5000,打造模具设计公司工作站虚拟化方案

客户为一家模具设计公司,使用 UG/NX 设计软件,现有10名设计师。当前面临以下问题:信息安全:设计图纸存在泄密风险,无法确保数据安全。 远程访问:设计师无法通过远程方式访问工作站进行设计工作。 资源共享:设计工作站目前归属于个人,设计资源难以高效共享,工作站资源利…

【日记】生巧好好吃!(701 字)

正文今天才发现昨天寒露。不过昨天很冷,今天倒挺暖和,太阳很好。下午上班,觉得这么好的太阳不做点什么很亏,于是转身回去拿了被子。晚上下班去收,被子上面落的全是桂花。那时候天色已暗,天边有一种低沉但通透的蓝。一般这种时候温度都很低了。秋天真的到了啊……主管买的…