黑马瑞吉外卖day3中用ThreadLocal解决获取不到当前用户id的技巧

本文介绍如何利用ThreadLocal实现跨类间ID传递,解决MyBatis Plus元数据处理器无法直接获取当前登录用户ID的问题。通过在过滤器类中设置线程局部变量,确保了不同类间能共享同一登录用户ID。

在数据库很多表中由于很多字段是公共的,比如创建时间,创建人id等,可以用一个元数据处理器进行公共字段的填充,该类如下:

package com.example.reggie.common;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler { //填充公共字段
    @Override
    public void insertFill(MetaObject metaObject) {
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        long id=Thread.currentThread().getId();
        log.info("-----{}",id);

        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
}

但是,这个类是获取不到当前登录用户的id的,因为它获取不到cookie,需要别的类传递给它id!

所以,该怎么解决这个id问题呢?

通过演示可知,在进行添加员工或更新员工操作时,过滤器、Controller类以及这个类使用的是同一个线程!

因此,有一个好的方法,使用ThreadLocal类(它是一个线程局部变量,每一个线程是一个作用范围)

可以创建一个线程局部变量工具类:

package com.example.reggie.common;

public class BaseContext {          //线程变量工具类
    private static ThreadLocal<Long> threadLocal=new ThreadLocal<>();

    public static void setCurrentId(Long id){
        threadLocal.set(id);
    }
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

在工具类中有两个静态方法,在其他类中可以使用。

在过滤器类中,当登录成功时,嵌入以下代码,获取当前登录id

Long empid=(Long) request.getSession().getAttribute("employee");
BaseContext.setCurrentId(empid);

然后在其他类中,调用BaseContext.getCurrentId()方法,即可成功完成不同的类之间的id的传递!

这样就解决了一些类不能获取到当前用户id的问题。

这样也解决了在不同的类之间传递参数的问题(前提是得经过同一个线程)。

### 瑞吉外卖项目中的 ThreadLocal 使用 #### 工具类 BaseContext 的编写 为了方便获取当前登录用户ID,在瑞吉外卖项目中创建了一个名为 `BaseContext` 的工具类,该类基于 `ThreadLocal` 封装而成。通过这种方式可以在线程范围内保存并访问特定的数据副本,从而确保每个请求都能独立持有自己的用户信息。 ```java public class BaseContext { private static final ThreadLocal<Long> CURRENT_USER_ID = new ThreadLocal<>(); public static void setCurrentUserId(Long userId) { CURRENT_USER_ID.set(userId); } public static Long getCurrentUserId() { return CURRENT_USER_ID.get(); } } ``` 此段代码展示了如何利用 `ThreadLocal` 来存储和检索当前线程内的用户ID[^3]。 #### LoginCheckFilter 中的应用 在过滤器 `LoginCheckFilter` 的 `doFilter` 方法里调用了上述提到的 `BaseContext.setCurrentUserId()` 函数来设定当前正在处理请求所对应的已认证客户的身份标识符: ```java @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = request.getHeader("Authorization"); // ...省略解析token逻辑... User user = userService.getUserByToken(token); // 获取用户信息 if (user != null){ BaseContext.setCurrentUserId(user.getId()); // 设置当前用户IdThreadLocal中 } filterChain.doFilter(request,response); } ``` 这段程序片段说明了当接收到HTTP请求时怎样验证令牌有效性并将关联客户的编号存入至 `ThreadLocal` 实例内以便后续操作能够轻易取得这些资料。 #### MyMetaObjectHandler 中获取用户ID 对于需要记录创建者或修改者的业务场景,则可以在继承自 `MyMetaObjectHandler` 类的地方重写相应的方法,并从中取出之前已经放入 `ThreadLocal` 容器里的用户身份标记来进行进一步的操作: ```java @Component public class CustomMetaObjectHandler extends MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject,"creator",()->BaseContext.getCurrentUserId(),"createUser"); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject,"updater",()->BaseContext.getCurrentUserId(),"updateUser"); } } ``` 这里展示的是每当有新纪录被加入数据库或是已有条目得到更新之际,都会自动填充由 `ThreadLocal` 所保持下来的使用者识别码作为创作者或者是最后编辑人的属性
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值