一,问题提出:
通常在项目设计开发中,用户登录后,我们会将用户的信息存到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