ThreadLocal

ThreadLocal提供线程局部变量,每个线程拥有自己独立的副本,不互相影响,常用于登录场景的临时用户存储。其API包括设置、获取和移除线程局部变量。在未登录情况下,利用ThreadLocal保存用户标识,如cookie的user-key,确保用户在不同操作间的状态保持。在拦截器中,检查ThreadLocal并根据情况设置临时用户或登录用户信息。
摘要由CSDN通过智能技术生成

原理

  • ThreadLocal只有当前线程可以访问,每个线程都有自己的变量副本。线程消亡他也消亡,他是变量对象,不是线程。
  • 其中存储的内容只有当前线程能访问的

API

  • void set(T value)
    设置当前线程的threadlocal的值
  • T get()
    该方法返回当前线程threadlocal的值
  • void removed()
    将当前线程threadlocal的值删除。
    目的是为了减少内存的占用。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度
  • T initialValue()
    返回该线程threadlocal的初始值,该方法是一个 protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用geto或 set(Object))时才执行,并且仅执行1次。 ThreadLocal中的默认实现直接返回一个null

场景使用ThreadLocal

在开发项目中,登录场景是不可避免的,但是再没登录的场景下也可以访问系统的相关功能,这就需要TheadLocal来进行对临时用户的存储及其鉴权。

思路

参考各个商城系统,在点击购物车时,会为临时用户生成一个name为user-key的cookie临时标识,过期时间为一个月,如果手动清除user-key,那么临时购物车的购物项也被清除,所以user-key是用来标识和存储临时购物车数据的

  • 首先明确每次拦截器都会重新设置threadlocal
  • 没有的话创建一个临时用户,回去的时候告诉用户的临时cookie。threadlocal中只封装临时用户信息
  • 有的话把临时用户和登录用户封装到一起,设置到threadlocal中

代码

package com.tomalen.gulimall.product.interceptor;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.tomalen.gulimall.product.constant.ProductConstant;
import com.tomalen.gulimall.product.entity.MemberRespVo;
import com.tomalen.gulimall.product.entity.UserInfoTo;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;

public class ProductInterceptor implements HandlerInterceptor {
    
    public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 准备好要设置到threadlocal里的user对象
        UserInfoTo userInfoTo = new UserInfoTo();
        HttpSession session = request.getSession();
        // 获取loginUser对应的用户value,没有也不去登录了。登录逻辑放到别的代码里,需要登录时再重定向
        MemberRespVo user = (MemberRespVo) session.getAttribute(ProductConstant.LOGIN_USER);
        if (user != null){ // 用户登陆了,设置userId
            userInfoTo.setUsername(user.getUsername());
            userInfoTo.setUserId(user.getId());
        }

        // 不登录也没关系,可以访问临时用户购物车
        // 去查看请求带过来的cookies里的临时购物车cookie
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0){
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                if(name.equals(ProductConstant.TEMP_USER_COOKIE_NAME)){
                    userInfoTo.setUserKey(cookie.getValue());
                    userInfoTo.setTempUser(true);
                }
            }
        }
        // 如果没有临时用户 则分配一个临时用户 // 分配的临时用户在postHandle的时候放到cookie里即可
        if (StringUtils.isEmpty(userInfoTo.getUserKey())){
            String uuid = UUID.randomUUID().toString().replace("-","");
            userInfoTo.setUserKey("GULI-" + uuid);//临时用户
        }
        threadLocal.set(userInfoTo);
        return true;
        // 还有一个登录后应该删除临时购物车的逻辑没有实现
    }

    /**
     * 执行完毕之后分配临时用户让浏览器保存
     */
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {

        UserInfoTo userInfoTo = threadLocal.get();
        // 如果是临时用户,返回临时购物车的cookie
        if(!userInfoTo.isTempUser()){
            Cookie cookie = new Cookie(ProductConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            // 设置这个cookie作用域 过期时间
            cookie.setDomain("gulimall.com");
            cookie.setMaxAge(ProductConstant.TEMP_USER_COOKIE_TIME_OUT);
            response.addCookie(cookie);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一个在努力为老板实现梦想的搬砖工

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

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

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

打赏作者

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

抵扣说明:

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

余额充值