之前一直不明白,为什么用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,这也是绝了,不过也正因为如此,才能使得不同线程之间不会相互干扰吧。
本文就分享到这里啦,有问题欢迎斧正。