ThreadLocal用于传递参数及优势

论坛 期权论坛 脚本     
已经匿名di用户   2022-5-29 19:36   1659   0

Threadlocal叫做线程本地变量,也叫线程本地存储,其作用是将参数储存在线程中,然后在该线程运行的任何阶段都能从线程中获取,简单的说,就是起到一个参数传递的作用。

举个具体运用的例子:一般用户在登陆系统之后,我们都会把一个用户的标识信息作为参数放入cookie、或session、或token中,返回给浏览器,然后在以后的请求中,就会带上这个标识信息。当在后续的请求中,我们需要用到这个标识信息时,我们可以从cookie、session、token中获取,并放入当前线程的Threadlocal中,最后就可以在Threadlocal中获取。优点是减少方法的入参个数。

(1)获取用户数据,并放入cookie中,同时返回result返回token信息(有可能浏览器屏蔽cookie),token就是对用户信息加密后数据。

    @PostMapping(value = "login")
    @ApiOperation(value = "登录", nickname = "login")
    public BaseResult<LoginResponse> login(@ApiParam @Valid @RequestBody LoginRequest request,
                                           HttpServletResponse httpServletResponse) {
        try {
            BaseResult<LoginResponse> result = loginService.login(request);
            /**
             * 登录完成回写登录token到cookie
             */
            if (result.isSuccess()) {
                Cookie cookie = CookieUtil
                        .createCookie(CommonConstant.KUC_TOKEN_COOKIE_KEY, result.getContent().getToken(),
                                maxAge, cookieDomain, true);
                httpServletResponse.addCookie(cookie);
            }
            return result;
        } catch (Exception e) {
            log.error("用户{}登录失败", request.getUserId(), e);
            throw e;
        }
    }

(2)拦截器:将HttpServletRequest中的token信息取出,并解码,然后存入到当前线程的ThreadlocalMap中。注意销毁


public class UserContextInterceptor implements HandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(UserContextInterceptor.class);


    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {
        //前端会将获取的token放到header中
        String token = httpServletRequest.getHeader("HIC_USER_CONTEXT");
        if (token != null) {
            try {
                //将token解码后的数据存入当前线程的ThreadLocalMap中
                UserContext.set(UserContext.getDecoder().decode(token));
            } catch (Exception var6) {
                logger.error("解析user context base64字符串失败,源字符串为:{}", token , var6);
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        UserContext.destroy();
    }
}

(3)工具类:作用是把信息从当前线程的ThreadlocalMap中取出或存入数据信息。


public class UserContext {
  
    public static final Encoder ENCODER = new Encoder();
    public static final Decoder DECODER = new Decoder();
    private static ThreadLocal<LoginUser> userThreadLocal = new ThreadLocal();

    public UserContext() {
    }

    public static LoginUser get() {
        return (LoginUser)userThreadLocal.get();
    }

    public static void set(LoginUser user) {
        userThreadLocal.set(user);
    }

    public static void destroy() {
        userThreadLocal.remove();
    }

    public static Encoder getEncoder() {
        return ENCODER;
    }

    public static Decoder getDecoder() {
        return DECODER;
    }
}

(4)当我们需要用到ThreadlocalMap中的数据时,我们可以在service层的实现类中直接获取。

@Service
public class XxxServiceImpl implements XxxService {

    @Override
    public BaseResult xxx() {
        LoginUser loginUser = UserContext.get();
        ……  ……  ……
    }
}

总结:Threadlocal就是起到一个传递参数的作用,它存在于线程的生命周期内。相对通过方法传递参数,它显得更隐蔽,同时也能减少方法的参数个数。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP