ThreadLocal

一、概述

在正常的 Web 开发过程中,对于每个接口,解析 Token,读取用户ID,每个方法都要传入 userId, 是不是也写过这种“流水线”代码?

private void handle(String token) {

    // 1. 从 token 中获取 userId
    Long userID = getUseIdFromToken(token);

    // 2. 验证用户的权限
    validateUserPermission(userID);

    // 3. 发起审批操作
    doExecute(userID);

    // 4. 记录操作日志
    saveOperationLog(userID);
}

private Long getUseIdFromToken(String token) {
    return 0L;
}

private void validateUserPermission(Long userId) {}

private void sendAppeal(Long userId) {}

private void saveOperationLog(Long userId) {}

当然了,这里的 token 实际上是从请求头或者其余方式读取到的。而这样的写法就会导致:

1)每一个接口,都需要写一遍获取并解析 Token 的业务逻辑

2)如果业务方法有需要用到 userId ,就需要在方法声明之中定义,一层一层往下传

为了解决这些问题,就需要用到 ThreadLocal,它是一个线程内部独享的变量,每个线程访问 ThreadLocal 的时候,访问的都是自己线程的私有变量。这样,我们的流程就可以改为:

1)解析 token,读取用户ID,将其放入都 ThreadLocal 中

2)业务方法从 ThreadLocal 直接取

接下来,我们首先来看一下具体的使用方式:

public static final ThreadLocal<Long> USERID_HOLDER = new ThreadLocal<>();

private void handle(String token) {
    // 1. 从 token 中获取 userId
    getUseIdFromToken(token);
    // 2. 验证用户的权限
    validateUserPermission();
    // 3. 发起审批操作
    sendAppeal();
    // 4. 记录操作日志
    saveOperationLog();
    // 5. 清理 ThreadLocal
    USERID_HOLDER.remove();
}

private void getUseIdFromToken(String token) {
    USERID_HOLDER.set(1L);
}

private void validateUserPermission() {
    Long userId = USERID_HOLDER.get();
}

private void sendAppeal() {
    Long userId = USERID_HOLDER.get();
}

private void saveOperationLog() {
    Long userId = USERID_HOLDER.get();
}

一套操作下来,代码量腰斩,可读性 + 可维护性直线上升。在上面的代码之中,我们有两点是需要考虑的:

1)ThreadLocal 为什么要定义为:satic final

2)当执行完成操作之后,为什么需要清理 ThreadLocal 的值?不清理会造成什么后果

posted @ 2025-12-20 03:00  不会Coding  阅读(1)  评论(0)    收藏  举报