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 层(拦截器)解析的数据,极大简化了代码结构。
相关推荐
小肥宅仙女3 小时前
限流方案
前端·后端
SadSunset3 小时前
关于springAOP的总结
java·后端·spring
计算机学姐3 小时前
基于SpringBoot的演出购票系统【2026最新】
java·vue.js·spring boot·后端·spring·tomcat·intellij-idea
用户6802659051193 小时前
2025年十大终端管理软件推荐指南
vue.js·后端·面试
ServBay3 小时前
.NET 10 与 C# 14 更新速览,代码更少,性能更好
后端·c#·.net
心在飞扬3 小时前
langchain学习总结-Model组件及使用技巧
后端
心在飞扬4 小时前
langchain学习总结-OutputParser组件及使用技巧
前端·后端
Penge6664 小时前
ClickHouse 向量化执行:为什么能让查询快几十倍?
后端
心在飞扬4 小时前
langchain学习总结-两个Runnable核心类的讲解与使用
前端·后端
大橘喵喵重4 小时前
FastAPI 实现国际化(i18n)和多语言支持的完整方案
后端