【谷粒商城之ThreadLocal用户身份鉴别】

本笔记内容为尚硅谷谷粒商城购物车ThreadLocal用户身份鉴别部分

目录

ThreadLocal

1.导入依赖

2.编写配置

3.配置Session 

4.cookie中的user-key说明 

5.编写To与常量

6.编写拦截器

7.添加拦截器的WebConfig配置类

8.Debug测试UserInfoTo中是否有数据 


ThreadLocal


ThreadLocal是一个池,也就是一个Map,存放的是一个线程下的共享数据,即同一个线程共享ThreadLocal中的数据

  • 核心原理是:Map<Thread,Object> threadLocal
  • 在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。线程之间互不干扰

  • 一次请求进来: 拦截器 ==>> Controller ==>> Service ==>> dao 用的都是同一个线程

 因为拦截器在拦截请求之后会向controller层传递一个UserInfoVo对象,所以为了获取到这个对象,使用ThreadLocal.

所以在拦截器处设置一个静态的ThreadLocal变量:

 public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<UserInfoTo>();

放上数据后就可以传递给后面。

1.导入依赖

导入redis和SpringSession的依赖

  • 将购物车数据存储至Redis中,因此,需要导入Spring整合Redis的依赖。项目上线之后,应该有一个专门的Redis负责存储购物车的数据不应该使用缓存的Redis。
  • 判断用户是否登录则通过判断Session中是否有用户的数据,因此,导入SpringSession的依赖
<!--  Spring整合Redis依赖      -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--  SpringSession的依赖  -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

2.编写配置

# 配置redis
spring.redis.host=192.168.10.10
spring.redis.port=6379

3.配置Session 

@EnableRedisHttpSession
@Configuration
public class GulimallSessionConfig {
    /**
     * 子域问题共享解决
     */
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        cookieSerializer.setDomainName("gulimall.com");
        cookieSerializer.setCookieName("GULIMALLSESSION");
        return cookieSerializer;
    }
 
    /**
     * 使用json序列化方式来序列化对象数据到redis中
     */
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
       return new GenericJackson2JsonRedisSerializer();
    }
}

4.cookie中的user-key说明 

用户第一次访问购物车的时候为用户颁发一个Cookie(无论是否登录都颁发),cookie的key就叫user-key,value在后台随机生成。同时要设置cookie的过期时间为1个月,还有cookie的作用域。如果手动清除user-key,那么临时购物车的购物项也被清除,所以user-key是用来标识和存储临时购物车数据的。 

 

用户登录之后点击购物车:

访问Session中存储的之前登录的用户信息,并进行用户身份信息(userId)的封装,userId只有登录过的UserInfoTo才有,没登陆过的这个属性值为null,这个属性就是购物车取数据时是取用户购物车还是临时购物车的一个判断。

用户未登录的时候点击购物车:

总之没登陆的用户userId为空,登录的userId有值,但是登不登陆其都有userKey这个属性的值。

Cookie中有 user-key,则表示有临时用户,访问临时用户的购物车信息,把user-key进行用户身份信息的封装

Cookie中没有 user-key,则表示没有临时用户

创建一个临时用户即生成一个name为user-key的cookie临时标识,过期时间为一个月,以后每次浏览器进行访问购物车的时候都会携带user-key。user-key 是用来标识和存储临时购物车数据的,处理完后会返回这个user-key

以上封装好的信息会放进ThreadLocal交给controller进行处理。

 

5.编写To与常量

 

6.编写拦截器

想要使用SpringMVC的拦截器机制,需要先编写一个类,实现HandlerInterceptor接口,根据业务需求重写接口中的方法。

拦截器作用为在调用购物车的接口(实际上拦截器拦截所有请求,不只是购物车的请求)前,先通过 Session 信息判断是否登录,并分别进行用户身份信息的封装,并把user-key放在 Cookie 中。

重写了两个方法:

  • 在执行目标方法之前,判断用户的登录状态,并封装传递给controller目标请求
  • 在执行目标方法之后,若没有临时用户则分配临时用户,让浏览器保存

同时,还要指定拦截器作用在那些请求上。

注意细节:整合SpringSession之后,Session获取数据都是从Redis中获取的

/**
 * 在执行目标方法前,先判断用户登录状态。并封装用户信息传递给Controller
 */
public class CartInterceptor implements HandlerInterceptor {

    public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();

    /*
    目标方法之前拦截
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        UserInfoTo userInfoTo = new UserInfoTo();
        HttpSession session = request.getSession();
        MemberRespVo member = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER);
        if (member != null) {
            //用户登录
            userInfoTo.setUserId(member.getId());
        }
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                if (name.equals(CartConstant.TEMP_USER_COOKIE_NAME)) {
                    userInfoTo.setUserKey(cookie.getValue());
                    userInfoTo.setTempUser(true);
                }
            }
        }
        //目标方法执行之前
        //如果没有临时用户,分配临时用户
        if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
            String uuid = UUID.randomUUID().toString();
            userInfoTo.setUserKey(uuid);
        }
        threadLocal.set(userInfoTo);
        return true;
    }

    /*
    业务执行之后
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        UserInfoTo userInfoTo = threadLocal.get();
        //如果没有任何信息,创建临时用户,保存cookie
        if (!userInfoTo.isTempUser()) {
            Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            cookie.setDomain("gulimall.com");
            //单位秒
            cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
            response.addCookie(cookie);
        }
    }


}

 

7.添加拦截器的WebConfig配置类

配置拦截器,否则拦截器不生效 

8.Debug测试UserInfoTo中是否有数据 

结束!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ThreadLocal是Java中的一个类,它提供了一种线程局部变量的机制,可以在每个线程中存储和访问自己的数据,而不会被其他线程访问到。ThreadLocal的主要作用是为每个线程提供独立的变量副本,使得每个线程都可以独立地修改自己的副本,而不会影响其他线程的副本。 使用ThreadLocal可以方便地在多线程环境下存储和获取用户数据。具体使用方法如下: 1. 创建ThreadLocal对象:可以通过直接实例化ThreadLocal类来创建一个ThreadLocal对象。 2. 存储数据:通过调用ThreadLocal对象的set方法,将数据存储到当前线程的ThreadLocalMap中。每个线程都有自己的ThreadLocalMap,用于存储该线程的ThreadLocal变量。 3. 获取数据:通过调用ThreadLocal对象的get方法,可以获取当前线程存储在ThreadLocalMap中的数据。 4. 清除数据:为了避免内存泄漏,使用完ThreadLocal后应该调用remove方法清除当前线程在ThreadLocalMap中的数据。 使用ThreadLocal存储用户数据的好处是: - 线程隔离:每个线程都有自己独立的数据副本,不会被其他线程访问到,保证了数据的线程安全性。 - 高效性:由于每个线程都有自己的数据副本,不需要进行线程同步操作,提高了程序的执行效率。 然而,需要注意的是,使用ThreadLocal也存在一些潜在的问题: - 内存泄漏:如果没有及时调用remove方法清除数据,可能会导致内存泄漏。 - 上下文传递:ThreadLocal只在当前线程内有效,无法在多个线程之间传递数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值