一种新的方法来存储用户信息——ThreadLocal

有目标就不怕路远。年轻人.无论你现在身在何方.重要的是你将要向何处去。只有明确的目标才能助你成功。没有目标的航船.任何方向的风对他来说都是逆风。因此,再遥远的旅程,只要有目标.就不怕路远。没有目标,哪来的劲头?一车尔尼雷夫斯基

导读:本篇文章讲解 一种新的方法来存储用户信息——ThreadLocal,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一,问题提出:

通常在项目设计开发中,用户登录后,我们会将用户的信息存到session,如果想在其它地方获取session中的用户信息,我们需要先获取HttpServletRequest,再通过request.getSession得到HttpSession。例如下代码:

public static User getSessionUser(HttpServletRequest request)
    {
        if(request.getSession().getAttribute( "sessionuser" ) != null)
        {
            return (User)request.getSession().getAttribute( "sessionuser" );
        }
        return null;
    }

但是这样操作会很麻烦并且存在并发问题

1.1 每次获取session中的用户信息,先手动获取到HttpServletRequest对象。

每次要获取session都要传递Request请求参数,尤其是service层或者dao层也要使用到user的信息,而通常在一个大型项目中,service层和dao层都是和web层分离开来,

都是单独的工程,不依赖servlet api,大家也不会为了在service层或者dao层获取登录用户信息而这么做,这样显得会很奇怪,所以我们只能在action中调用service的时候,将用户信息以参数形式传过去。

对于session中的用户信息,我们不仅想要在action中随用随取,还想在其它普通类中取,即使不依赖servlet api, 我们也要在方法里随用随取,我们在处理请求的时候,很多操作都要获取当前用户的ID等信息,由上可见,我们凡是在action的方法中任何一处想要获取session中的用户信息,则必须要先手动获取到HttpServletRequest,是不是比较麻烦

1.2 如果遇到高并发,多人同时登录系统时,会出现session混乱。

考虑到并发,如果两个人或者多人同时登录系统,A置成自己的session了,B又置成他的session了,两人开始打架了。

二,解决方案

客户端发送的每次http请求,对应的服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程。

我们引入ThreadLocal,首先它不是用来解决多线程并发共享一类问题的,它解决的是在一个线程中参数的传递。

2.1 ThreadLocal

顾名思义,就是本地线程,可是这个名字实在容易让人误解,因为其实它是本地线程局部变量的意思,首先我们要知道,我们每个请求都会对应一个线程,

这个ThreadLocal就是这个线程使用过程中的一个变量,该变量为其所属线程所有,各个线程互不影响。

这里我们要了解一下ThreadLocal的三个方法:

ThreadLocal.set(T value); //设置值

ThreadLocal.get(); //获取值

ThreadLocal.remove(); //移除值

具体实例:

package com.tigerhhzz.wuaimai.common;


/**
 * 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
 */
public class BaseContext {


    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    /**
     * 设置值
     * @param id
     */
    public static void setCurrentId(Long id){
        threadLocal.set(id);
    }

    /**
     * 获取值
     * @return
     */
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

所以我们可以借助这个ThreadLocal来存储登录用户的信息,在一个请求中,所有调用的方法都在同一个线程中去处理,这样就实现了在任何地方都可以获取到用户信息了,从而摆脱了HttpServletRequest的束缚。

2.2 避免了跨层之间的参数传递,实现了层与层之间的松耦合。

ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立的改变自己的副本,而不会影响其他线程所对应的副本。

ThreadLocal为每个线程提供了单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

2.3 项目实际应用

项目结构如下:
在这里插入图片描述

BaseContext .class

package com.tigerhhzz.wuaimai.common;


/**
 * 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
 */
public class BaseContext {


    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    /**
     * 设置值
     * @param id
     */
    public static void setCurrentId(Long id){
        threadLocal.set(id);
    }

    /**
     * 获取值
     * @return
     */
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

logincheckfilter部分代码:

        //4、判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("employee") != null){
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));

            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);

            filterChain.doFilter(request,response);
            return;
        }

我们可以在logincheckfilter的dofilter方法中获取当前登录用户id,并调用ThreadLocal方法来设置当前线程的线程局部变量的值(用户id),然后在mymetaobjecthandler的update方法中调用threadlocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。

MyMetaObjecthandler 公共字段填充类

package com.tigerhhzz.wuaimai.common;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

/**
 * 自定义元数据对象处理器
 */
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    /**
     * 插入操作,自动填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充[insert]...");
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }

    /**
     * 更新操作,自动填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]...");
        log.info(metaObject.toString());

        long id = Thread.currentThread().getId();
        log.info("线程id为:{}",id);

        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/135702.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!