之前一直不明白,为什么用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,这也是绝了,不过也正因为如此,才能使得不同线程之间不会相互干扰吧。

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

posted on 2022-05-04 10:20  剽悍一小兔  阅读(5)  评论(0编辑  收藏  举报  来源