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 层(拦截器)解析的数据,极大简化了代码结构。
相关推荐
小江的记录本18 小时前
【MyBatis-Plus】MyBatis-Plus的核心特性、条件构造器、分页插件、乐观锁插件
java·前端·spring boot·后端·sql·tomcat·mybatis
驕傲的兎孒18 小时前
基于 SpringBoot + Vue3 + AI 打造企业级售后服务支持平台 | 实战方案分享
人工智能·spring boot·后端
大傻^18 小时前
Spring AI Alibaba 可观测性实践:AI应用监控与链路追踪
java·人工智能·后端·spring·springaialibaba
诗人不写诗18 小时前
spring是如何组织切面的
java·后端·spring
小杨同学4918 小时前
STM32 进阶封神之路(二十二):DMA 实战全攻略 ——ADC 采集 + 串口收发 + 内存复制(库函数 + 代码落地)
后端·单片机·嵌入式
天下无贼!19 小时前
【Python】2026版——FastAPI 框架快速搭建后端服务
开发语言·前端·后端·python·aigc·fastapi
大傻^19 小时前
Spring AI Alibaba Agent开发:基于ChatClient的智能体构建模式
java·数据库·人工智能·后端·spring·springaialibaba
大傻^19 小时前
Spring AI Alibaba ChatClient实战:流式输出与多轮对话管理
java·人工智能·后端·spring·springai·springaialibaba
SuniaWang19 小时前
《Spring AI + 大模型全栈实战》学习手册系列· 专题二:《Milvus 向量数据库:从零开始搭建 RAG 系统的核心组件》
java·人工智能·分布式·后端·spring·架构·typescript
张小洛20 小时前
Spring 常用类深度剖析(工具篇 02):ReflectionUtils——优雅操作反射的利器
java·后端·spring·工具类·spring常用类