ThreadLocal有哪些应用场景,开源框架中都是如何使用的?

公众号:Hoeller,有精品面试题(不是很多,150道,但很经典)

前几天发了一篇关于ThreadLocal和ScopedValue的文章,发现很多同学对ThreadLocal在实战中一般会用在那些场景不太清楚,所以这篇文章我结合我看过的源码来给大家总结一下。

第一种场景:Spring事务

先看第一种场景,直接上代码:

这是再简单不过的Spring事务的代码,那么执行上面两个SQL的数据连接是谁创建的呢?

答案是Spring,而不是JdbcTemplate或Mybatis。为什么?假如数据库连接是JdbcTemplate创建的,它是不会去修改数据库连接的autocommit属性的,这个属性的值将是true ,那么执行完一个SQL就会自动提交,等到抛NullPointerException时,由于SQL已经提交了也就不会回滚了,也就没有达到Spring事务想要的效果。

所以,Spring需要自己创建数据库连接,并将它的autocommit属性改为false ,并且JdbcTemplate或者Mybatis需要拿到Spring所创建的数据库连接,这样执行SQL时才不会自动提交,等到执行完整个方法后,Spring才判断是需要回滚还是提交,反正Connection连接对象是Spring自己创建的,所以Spring自己调用Connection的commit()或rollback()方法即可。

Spring为了能够让JdbcTemplate或者Mybatis拿到数据库连接,所以会将数据库连接存到ThreadLocal中,JdbcTemplate或者Mybatis从ThreadLocal获取即可。

第二种场景:分布式事务

再来看第二种场景,一般分布式事务的设计中,都会包含两种ID:全局ID和分支ID。

每个微服务需要创建自己的分支id(branchId),但是微服务A作为全局事务的发起者,它还要负责创建一个表示该全局事务的id,也就是全局id(globalId),并且微服务A需要将全局id传递给下游,从而使得微服务B和微服务C知道自己的分支事务是属于哪个全局事务的。

如何传递呢?比如调用微服务的是http请求,则把全局id添加到请求头即可,如果是其他rpc协议,比如dubbo协议,也可以利用它的扩展机制将全局id添加到请求数据中传递给下游服务。

重点在于下游微服务B如何接收全局id呢?像Seata中用的就是mvc拦截器,拦截请求取出请求头中的全局id,并保存到ThreadLocal中,后续逻辑中,比如注册分支事务到全局事务管理器时,直接从ThreadLocal中取出全局id即可。

第三种场景:AopContext和RpcContext

在Spring Aop中,如果想要拿到代理对象,可以通过以下代码拿到:

其中核心就是AopContext.currentProxy(),它能够拿到当前在调用test()方法的代理对象,而它的内部用的也是ThreadLocal,比如该方法的源码为:

其中currentProxy是一个常量为:

其中NamedThreadLocal是Spring自己定义的:

和AopContext类似的还有Dubbo中的RpcContext,Dubbo服务被调用时可以通过RpcContext拿到服务消费者的URL等信息,它们的共同点是:方便在业务代码中获取框架内部生成的对象。

第四种场景:SpringBoot 3.0

SpringBoot3.0中在SpringBoot启动过程中新增了一个机制,叫做:SpringApplicationHook ,通过这个机制可以用来在项目启动时设置启动监听器,比如:

withHook()方法有两个参数,第一个参数是一个SpringApplicationHook对象,第二个参数是一个Runnable对象,都是函数式接口,所以上面代码都传入的Lambda表达式,上面代码运行时会先启动SpringBoot,在启动的最后会执行started()方法中的打印语句。

而withHook()方法会把SpringApplicationHook对象设置到ThreadLocal中,在SpringBoot启动过程的最后会从ThreadLocal中取出SpringApplicationHook对象并执行。

自然,肯定还有很多场景和源码中会用到ThreadLocal,大家可以把自己遇到的分享在评论区哦。

我是爱读源码的大都督周瑜,欢迎关注我的公众号:Hoeller。公众号里有跟多高质量干货系列文章和精品面试宝典。

相关推荐
panxianren31 分钟前
Bun 1.4 的 bun test --parallel 将 4 秒的测试套件缩短到约 1 秒
java·javascript
码事漫谈2 小时前
骂AI,会让它做得更好吗?
后端
sunshine22 girl2 小时前
Java学习七 Java 项目实战2-注册业务
java·学习
陈随易2 小时前
bm2,Node.js 与 Bun 项目部署新选择
前端·后端·程序员
省钱兄--zs3 小时前
北京24小时自助健身房系统软件开发实战:从架构设计到部署指南
java·数据仓库·spring boot·小程序·需求分析
youdexiang3 小时前
Android录音软件时间关键词检索功能分析
java·人工智能
架构技术专栏4 小时前
AI Agent 框架怎么选:从一次制度查询拆出技术边界
后端·面试
sunshine22 girl4 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
code2cat4 小时前
Java进阶篇之AtomicStampedReference:引用回到原值,版本戳仍能记住变化
java·cas·并发编程·aba
泡茶喝茶写代码5 小时前
A股量化数据工程:从 REST 接口到策略信号(第 8 篇):成长能力因子:营收与利润增速
java·python·股票数据api·股票数据api接口·股票量化数据api·股票量化数据接口·股票数据api数据