0
点赞
收藏
分享

微信扫一扫

不用session也能随时获取globalUser,用ThreadLocal也太香了


之前一直不明白,为什么用shiro之类的框架,为什么我们可以在任何地方,只要写上一句

SubjectUtil.getCurrentUser,就可以得到当前的登录用户。按照道理,最初学web的时候,都会被告知有一个叫做session的东西,然后通过request对象就可以得到session,用户登录后,把用户信息存到session里面就可以了。

代码一般是这样:

request.getSession().getAttribute('currentUser');

可是这样就有个麻烦的地方,如果我是在某个service要用到当前用户,就得把request作为参数传进去,很不雅。

而看到shiro这一类的框架我就犯迷糊,我TM似乎也没看到什么地方用session啊,他凭什么可以写一句SubjectUtil.getCurrentUser之类的代码,就搞定了?

后来百度了才知道,原来这类框架都是用ThreadLocal解决这个问题的。

ThreadLocal是java.lang包里面的,所以不用导包直接就可以用,它的作用是在当前线程中开辟一个临时空间,只要是在一个线程中,就可以随取随用。

比如,新建一个MyThreadLocal:

public class MyThreadLocal {

        public static ThreadLocal<User> globalUser = new ThreadLocal<User>();

}

里面维护一个ThreadLocal变量,因为要给其他地方使用,所以设置为static。

什么时候赋值呢?

假设是前后端分离的系统,前端需要送token过来鉴权,那么可以设置一个过滤器或者拦截器,只要请求过来就根据token去redis之类的缓存中获取用户信息,塞到globalUser中,例如:

@Override

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {

String token = req.getParameter("token");

Object user = RedisUtil.get(token);

if(user != null) {

MyThreadLocal.globalUser.set((User) user);

}

chain.doFilter(req, resp);

}

再搞一个SubjectUtil

public class SubjectUtil {

public User getCurrentUser() {

return MyThreadLocal.globalUser.get();

}

}

于是,一个请求就是一个线程,你在任何地方都可以这样得到用户信息,而无需用到session(因为用session会有很多问题的,现在做项目基本不用session了)

public void userList() {

User user = new SubjectUtil().getCurrentUser();

System.out.println(user.getUsername() + "查询了1次用户列表!");

}

最后再来看看ThreadLocal为何能这么牛?

看下他的get方法:

  

public T get() {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null) {

ThreadLocalMap.Entry e = map.getEntry(this);

if (e != null) {

@SuppressWarnings("unchecked")

T result = (T)e.value;

return result;

}

}

return setInitialValue();

}

原来,ThreadLocal内部有个ThreadLocalMap,这是存储所有的线程本地变量的,它不是HashMap,而是ThreadLocal内部的一个静态内部类。但是,它的作用和HashMap差不多。

这个内部Map的key就是每一个当前线程ThreadLocal的地址,这一点可以从set方法中看到

public void set(T value) {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

}

把当前线程作为key,这也是绝了,不过也正因为如此,才能使得不同线程之间不会相互干扰吧。

本文就分享到这里啦,有问题欢迎斧正。

举报

相关推荐

0 条评论