BaseContext:如何在Service层“隔空取物”获取当前登录用户ID?

在做Spring Boot 的 Web 项目时,在 Controller 或 Service 层经常会看到这样一行代码:

ini 复制代码
// 在 Service 层直接获取当前登录用户的ID 
Long userId = BaseContext.getCurrentId();

这就很神奇了:

  1. 没有传参 :Controller 调用 Service 时,并没有把 userId 作为参数传进来
  2. 没有查库:这一行代码也没有去查询数据库
  3. 数据准确:它总是能精准地拿到当前发送请求的那个用户的 ID,张三发请求拿到张三,李四发请求拿到李四,互不干扰

它是怎么做到的?

有两个核心概念:ThreadLocal 和 Tomcat 的"一请求一线程"模型。

1.容器:ThreadLocal (线程局部变量)

BaseContext 只是一个包装类,它内部的核心是 JDK 提供的 ThreadLocal。

csharp 复制代码
public class BaseContext {
    // 核心:ThreadLocal 对象
    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id) {
        threadLocal.set(id); // 存入
    }

    public static Long getCurrentId() {
        return threadLocal.get(); // 取出
    }
    
    public static void removeCurrentId() {
        threadLocal.remove(); // 清除
    }
}

ThreadLocal 的作用:

当我们在 线程 A 中往 ThreadLocal 存入数据时,只有 线程 A 能取出来

线程 B 即使访问同一个变量,也完全摸不到 线程 A 的数据

这就是线程隔离(Thread Safety)

2.环境:Tomcat 的"一请求一线程"模型

Spring Boot 内置的 Web 服务器通常是 Tomcat。Tomcat 处理请求的机制是:One Request, One Thread (一个 HTTP 请求,由一个独立的线程全程负责)

当一个用户发起请求(比如"添加购物车")时:

Tomcat 分配 线程 X 来处理这个请求

拦截器 (Interceptor) 是 线程 X 执行的

Controller 是 线程 X 执行的

Service 还是 线程 X 执行的

Mapper 依然是 线程 X 执行的

结论: 只要我们没有手动开启新线程(new Thread),整个后端业务流程就像一场接力赛,但是是同一个运动员(线程 X) 从头跑到尾

流程

基于以上两个原理,我们可以还原 userId 是如何从请求头一步步流转到 Service 层的:

第一步:拦截器 (存入)

请求刚到达后端,拦截器(JwtTokenUserInterceptor)最先拦截

第二步:Controller

拦截器放行后,代码进入 Controller

第三步:Service (取出)

代码进入 Service 层

为什么要这么设计?

使用 ThreadLocal (BaseContext) 的方案,实现了数据在同一线程内的"隐式传递",让代码极其简洁优雅。

总结

  1. BaseContext 利用 ThreadLocal 实现了线程内部的数据隔离存储。
  2. Tomcat 保证了从拦截器到 Service 处于 同一个线程 中。
  3. 二者结合,让我们可以在 Service 层"隔空"获取 Controller 层(拦截器)解析的数据,极大简化了代码结构。
相关推荐
MC丶科1 天前
【SpringBoot常见报错与解决方案】中文乱码?Spring Boot 统一解决前后端中文乱码问题(含 Postman 测试)!别再百度“加 UTF-8”了!
spring boot·后端·postman
XXOOXRT1 天前
基于SpringBoot的加法计算器
java·spring boot·后端·html5
moxiaoran57531 天前
Go语言的错误处理
开发语言·后端·golang
短剑重铸之日1 天前
《7天学会Redis》特别篇: Redis分布式锁
java·redis·分布式·后端·缓存·redission·看门狗机制
小北方城市网2 天前
SpringBoot 全局异常处理与接口规范实战:打造健壮可维护接口
java·spring boot·redis·后端·python·spring·缓存
hanqunfeng2 天前
(三十三)Redisson 实战
java·spring boot·后端
小北方城市网2 天前
SpringBoot 集成 MyBatis-Plus 实战(高效 CRUD 与复杂查询):简化数据库操作
java·数据库·人工智能·spring boot·后端·安全·mybatis
hanqunfeng2 天前
(四十)SpringBoot 集成 Redis
spring boot·redis·后端
小北方城市网2 天前
SpringBoot 集成 MinIO 实战(对象存储):实现高效文件管理
java·spring boot·redis·分布式·后端·python·缓存
程序员泠零澪回家种桔子2 天前
RAG自查询:让AI精准检索的秘密武器
人工智能·后端·算法