ThreadLocal保存用户信息

41 篇文章 1 订阅
10 篇文章 0 订阅

1.介绍

在并发请求情况下,因为每次请求都有不同的用户信息,我们必须保证每次请求保存的用户信息互不干扰,线程独立。注意:这里不是解决多线程资源共享问题,而是要保证每个线程都有自己的用户资源,互不干扰

ThreadLocal的作用主要是做数据隔离,填充的数据只属于当前线程,变量的数据对别的线程而言是相对隔离的,在多线程环境下,如何防止自己的变量被其它线程篡改。

而JDK中提供的ThreadLocal恰好满足这个需求,那么ThreadLocal是如何实现这一需求的呢?

关键点:

  • 每个线程(Thread)内部都持有一个ThreadLocalMap对象。
  • ThreadLocalMap的Key是某个ThreadLocal对象,值是任意Object。
  • 不同线程,内部有自己的ThreadLocalMap,因此Map中的资源互相不会干扰。

数据在堆栈中的存储示意图:

2.基本使用

public class UserHolder {
    private static final ThreadLocal<Long> TL = new ThreadLocal<>();

    public static void setUserId(Long userId) {
        TL.set(userId);
    }

    public static Long getUserId() {
        return TL.get();
    }

    public static void removeUserId() {
        TL.remove();
    }
}

3. 工具类

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


 /**
 * @author 暗余
 * @date 2021/7/17 15:44
 */
@SuppressWarnings("unused")
public final class ThreadLocalUtils {

    private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL =
            ThreadLocal.withInitial(() -> new ConcurrentHashMap<>(16));

    /**
     * 获取到ThreadLocal中值
     *
     * @return ThreadLocal存储的是Map
     */
    public static Map<String, Object> getThreadLocal() {
        return THREAD_LOCAL.get();
    }

    /**
     * 从ThreadLocal中的Map获取值
     *
     * @param key Map中的key
     * @param <T> Map中的value的类型
     * @return Map中的value值 可能为空
     */
    public static <T> T get(String key) {
        return get(key, null);
    }

    /**
     * 从ThreadLocal中的Map获取值
     *
     * @param key          Map中的key
     * @param defaultValue Map中的value的为null 是 的默认值
     * @param <T>          Map中的value的类型
     * @return Map中的value值 可能为空
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(String key, T defaultValue) {
        Map<String, Object> map = THREAD_LOCAL.get();
        if (MapUtils.isEmpty(map)) {
            return null;
        }
        return (T) Optional.ofNullable(map.get(key)).orElse(defaultValue);
    }

    /**
     * ThreadLocal中的Map设置值
     *
     * @param key   Map中的key
     * @param value Map中的value
     */
    public static void set(String key, Object value) {
        Map<String, Object> map = THREAD_LOCAL.get();
        map.put(key, value);
    }

    /**
     * ThreadLocal中的Map 添加Map
     *
     * @param keyValueMap 参数map
     */
    public static void set(Map<String, Object> keyValueMap) {
        Map<String, Object> map = THREAD_LOCAL.get();
        map.putAll(keyValueMap);
    }

    /**
     * 删除ThreadLocal中的Map 中的value
     *
     * @param key Map中的key
     */
    public static void delete(String key) {
        Map<String, Object> map = THREAD_LOCAL.get();
        if (MapUtils.isEmpty(map)) {
            return;
        }
        map.remove(key);
    }

    /**
     * 删除ThreadLocal中的Map
     */
    public static void remove() {
        THREAD_LOCAL.remove();
    }

    /**
     * 从ThreadLocal中的Map获取值 根据可key的前缀
     *
     * @param prefix key 的前缀
     * @param <T>    Map中的value的类型
     * @return 符合条件的Map
     */
    @SuppressWarnings("unchecked")
    public static <T> Map<String, T> fetchVarsByPrefix(String prefix) {
        Map<String, T> vars = new HashMap<>(16);
        if (StringUtils.isBlank(prefix)) {
            return vars;
        }
        Map<String, Object> map = THREAD_LOCAL.get();
        if (MapUtils.isEmpty(map)) {
            return vars;
        }
        return map.entrySet().stream().filter(test -> test.getKey().startsWith(prefix))
                .collect(Collectors.toMap(Map.Entry::getKey, time -> (T) time.getValue()));
    }

    /**
     * 删除ThreadLocal中的Map 中的Value  按 Map中的Key的前缀
     *
     * @param prefix Map中的Key的前缀
     */
    public static void deleteVarsByPrefix(String prefix) {
        if (StringUtils.isBlank(prefix)) {
            return;
        }
        Map<String, Object> map = THREAD_LOCAL.get();
        if (MapUtils.isEmpty(map)) {
            return;
        }
        map.keySet().stream().filter(o -> o.startsWith(prefix)).collect(Collectors.toSet()).forEach(map::remove);
    }
}

4. 例子

package net.facelib.eam.plancenter.webController.config;

import com.alibaba.fastjson.JSONObject;
import net.facelib.eam.*;
import net.facelib.eam.plancenter.common.MyPlanConstant;
import net.facelib.eam.plancenter.common.PlanException;
import net.facelib.eam.plancenter.webController.utils.ServletUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author lc
 * @version 1.0
 * @date 2022/7/5 9:23
 */
@Aspect
@Component
@Order(1)
public class PermissionAspect {
    private static Logger logger = LoggerFactory.getLogger(PermissionAspect.class);
    private static ThreadLocal<Map<String, Object>> userInfo = new ThreadLocal<>();

    @Autowired
    protected ITokenAccessor tokenAccessor;

    @Pointcut("execution(* net.facelib.eam.plancenter.webController.service..*.*(..))")
    public void pointCut(){
    }

    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        try {
            String tokenid = ServletUtils.getRequest().getHeader("tokenid");
            Token token = tokenAccessor.getToken(tokenid);
            Map<String, Object> map = new HashMap<>();
            map.put("userId", token.getId());
            map.put("userName", token.getName());
            map.put("props", token.getProps());
            map.put("tokenid", tokenid);
            userInfo.set(map);
        } catch (Exception e){
            logger.error("token解析失败"+ e);
            throw new PlanException(EamErrorType.EAM_INVALID_TOKEN.getValue(), MyPlanConstant.PC_USER_TOKEN_ERROR, e);
        }
    }

    public static Long getUserId() {
        Map<String, Object> map = userInfo.get();
        Long userId = Long.valueOf(map.get("userId").toString());
        return userId;
    }

    public static String getUserName() {
        Map<String, Object> map = userInfo.get();
        return map.get("nickName").toString();
    }

    public static JSONObject getProps() {
        Map<String, Object> map = userInfo.get();
        JSONObject jsonObject = JSONObject.parseObject(map.get("props").toString());
        return jsonObject;
    }

    public static String getTokeid() {
        Map<String, Object> map = userInfo.get();
        String tokenid = map.get("tokenid").toString();
        return tokenid;
    }
}

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LC超人在良家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值