Bootstrap

构建更清晰的业务逻辑:使用ThreadLocal优化用户信息访问模式

ThreadLocal介绍

ThreadLocal是Java提供的一个类,它为每个线程提供了一个独立的变量副本。这意味着即使多个线程同时访问同一个ThreadLocal实例,它们之间也不会相互干扰。这对于需要在线程生命周期内保持某些数据一致性的情况非常有用。处理并发请求的Web应用中,可以安全且高效地管理每个请求的上下文信息(如用户ID).

选择ThreadLocal原因

在多线程环境下,直接共享对象可能会导致数据竞争和不一致问题。而使用ThreadLocal可以确保每个线程都有自己的变量副本,从而避免这些问题。特别是在Web应用程序中,每个请求通常由不同的线程处理,这时使用ThreadLocal来存储与请求相关的上下文信息(例如用户ID)就显得特别合适。

实现步骤

定义ThreadLocal工具类

首先,我们需要定义一个包含ThreadLocal<Long>变量的工具类,用于存储和检索用户ID。

public class BaseContext {

    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();
    }

}

 设置当前线程的用户ID

接下来,在请求到达时设置用户ID。这可以通过Spring MVC过滤器或拦截器实现。下面是一个简单的拦截器示例(在jwt令牌中校验中加入用户id):

  try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前员工的id:{}", empId);
            BaseContext.setCurrentId(empId);
            //3、通过,放行
            return true;

 在业务逻辑中使用

现在,在业务逻辑层的任何地方都可以通过调用BaseContext.getCurrentId()来获取当前线程关联的用户ID了。

  Long empId = BaseContext.getCurrentId();
        
        employeeMapper.insert(employee);

在serviceImpl层可以通过提供的工具类直接获取到用户ID。

注意事项与结论

清理ThreadLocal:务必记得在适当的时候清除ThreadLocal变量,尤其是在Servlet容器重用线程的情况下,以避免潜在的内存泄漏。

线程安全性:虽然ThreadLocal本身是线程安全的,因为它为每个线程提供了独立的变量副本,但如果你在复杂的并发环境中操作共享资源,则仍需注意同步问题

通过使用ThreadLocal,我们可以轻松地在线程生命周期内管理和访问特定于该线程的数据,比如用户ID。这种方法不仅提高了代码的可维护性,还增强了系统的性能和安全性。然而,正确地管理和清理ThreadLocal变量同样重要,以避免内存泄漏等问题。

;