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。公众号里有跟多高质量干货系列文章和精品面试宝典。

相关推荐
葫芦和十三23 分钟前
图解 MongoDB 08|ESR 原则:复合索引的字段顺序怎么定
后端·mongodb·agent
葫芦和十三8 小时前
图解 MongoDB 07|索引类型:七种索引,七种访问形状
后端·mongodb·agent
朦胧之9 小时前
AI 编程-老项目改造篇
java·前端·后端
爱勇宝12 小时前
我做了一个只用来搜歌词的小 App
android·前端·后端
IT_陈寒13 小时前
SpringBoot自动配置坑了我一晚上,原来问题出在这
前端·人工智能·后端
SelectDB14 小时前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
SelectDB14 小时前
秒级弹性、最高降本 70%:SelectDB Serverless 如何重塑云数仓资源效率
大数据·后端·云原生
程序猿大帅14 小时前
别再只当调包侠了:用 Spring AI 落地 Function Calling,我被大模型硬生生砸出了三个大坑
java
PinkSun14 小时前
Spring AI ChatMemory踩坑实录:重启丢数据、Agent丢记忆、对话溢出
后端·ai编程
壹方秘境14 小时前
我用Go语言开发了一个跨平台的HTTPS抓包和调试工具
前端·后端·ios