现象:

  1. 之前想要拿到当前线程中的请求,直接在工具类中放了个ThreadLocal作为容器,当时候的需求只需要拿到request即可,所以那个方式是可以的。<br /> 但我们在做的这个系统中需要更多的东西,比如会把用户对象放到session中,在判断是否为当前用户这种情况下就比较常用,那之前的代码就不太适合了,因为作为容器的ThreadLocal中只能将request作为参数set进去,session是进不去的。

方式一:

一种解决方案是修改工具类中的容器,改用RequestContextHolder中的ServletRequestAttributes来存放「用于存放用户信息的session」。
改进后的代码:

public static HttpSession getSession(){
        ServletRequestAttributes rat = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = rat.getRequest();
        HttpSession session = request.getSession();
        return session;
    }
  1. RequestContextHolder类
    Holder class to expose the web request in the form of a thread-bound RequestAttributes object.
    简单翻译下,这个类的作用是通过操作RequestAttributes请求属性这个对象(绑定了线程)来间接处理请求相关的一些东西。
    所以可以看到代码中首先拿到了RequestAttributes对象,当然这里要转成Servlet类型。
    2. ServletRequestAttributes类
    如果不转型,那个attributes类是无法方便操作request、session这些原生servlet相关的对象或者属性的,因为本身Java Web最原始的实现就是servlet形式的,Spring框架当然会为其做特定的一些封装,也就是这个类的来源。
    代码中首先通过属性拿到了HttpServletRequest对象,然后通过请求对象拿到session。
    这里有个小问题,那就是既然里面已经有一个getSession(boolean allowCreate)方法了,那为啥不直接获取session对象呢?
    我简单回去看了下源码,这个方法上带了一个布尔类型的参数,含义是是否允许创建session,在方法逻辑内部也调用了这个方法,然后也会发现在对于attribute的操作方法,如 setAttribute+removeAttribute+getAttributeNames+updateAccessedSessionAttributes中都调用了getSession,然后根据不同的逻辑传入true或者false,也就是说,这个getSessioin本身不是为我们所写,它的存在是为了完善这一套的逻辑,建立起请求、回话、属性之间的前后逻辑。所以我们不直接调用它,因为你不知道该传true还是false。

  2. NamedThreadLocal
    最后一个关键点就是,RequestContextHolder如何绑定线程,说白了,它也是组合了ThreadLocal,在setAttribute中本质上也是把value放到ThreadLocalMap中,相关源码如下:

    private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
             new NamedThreadLocal<RequestAttributes>("Request attributes");
    public void set(T value) {
         Thread t = Thread.currentThread();
         ThreadLocalMap map = getMap(t);
         if (map != null)
             map.set(this, value);
         else
             createMap(t, value);
     }
    

    在近期的项目开发中,又遇到上述的开发逻辑,个人观点认为过于复杂了

方式二:

   ThreadLocal是指获取当前线程,相当于一个局部线程,一次封装,后面可以直接进行获取当前线程对象<br />       在整体的项目开发过程中,可以在网关上引入ThreadLocal,将当前用户的主键封装到用户对象中,后续可用过ThreadLocal获取用户对象,若是想获取用户对象详细信息,可以查询数据库,除此之外,网关上也需要配合拦截器进行一些处理,这也算一个实例,相比于方法一简洁一些