thread类中有:ThreadLocal.ThreadLocalMap threadLocals = null;
ThreagLocal的方法 public void set(T value) { Thread t = Thread.currentThread(); //获取当前线程 ThreadLocalMap map = getMap(t); //获取threadLocals ,就是上面的那个对象 if (map != null) //第一次以外设置就不为空,把当前的ThreadLocal对象作为key value作为值存入,也可以说是更新值 map.set(this, value); else createMap(t, value);//第一次设置就是为空的,执行方法看下面 }
第一次设置时执行的,创建一个新的ThreadLocalMap,并且把当前对象也就是 声明的ThreadLocal对象 private static final ThreadLocal<UserInfo> tl = new ThreadLocal<>();作为key,set的值作为value存进去。
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
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(); }
static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
thread的ThreadLocal.ThreadLocalMap threadLocals(这个是threadlocal的内部类)会把每一个threadlocal对象作为key。一个threadlocal只能对应一个值,thread的ThreadLocal.ThreadLocalMap threadLocals中可以存储多个threadlocal (也就是一个线程可以有多个threadlocal对象) 也就是下图这样子。可能解释的不太清楚,还有就是threadlocal本身不保存数据,都是保存在thread的ThreadLocal.ThreadLocalMap threadLocals上的,获取和设置值的时候threadlocal通过方法获取当前线程设置/获取以自己为key的对应的值
thread和request对比
Web容器中有三个周期 request/Httpsession/application
其中request是客户端发出的一个请求,这个request的载体就是一个 线程,实际等同于一个线程的生命周期。Request是封装在线程上面一个抽象概念。
而ThreadLocal则相当于 多个线程的一个共享全局变量存储地,它里面保存的是和每个线程相关的状态。所以threadLocal是为线程服务的,和线程处于一个底层位置。
当一个线程或request结束时,threadlocal中的状态就没有了,所以threadLocal基本 类似request.setAttribute作用,threadLocal中的对象状态的生命周期等同于request.
使用ThreadLocal可以大量减少参数的传递(),可以 使代码简洁。。。。
实战:登陆鉴权
登陆拦截器 public class LoginInterceptor extends HandlerInterceptorAdapter { private JwtProperties jwtProperties; // 定义一个线程域,存放登录用户 private static final ThreadLocal<UserInfo> tl = new ThreadLocal<>(); public LoginInterceptor(JwtProperties jwtProperties) { this.jwtProperties = jwtProperties; } //preHandle,处理器执行前调用 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 查询token String token = CookieUtils.getCookieValue(request, "LY_TOKEN"); if (StringUtils.isBlank(token)) { // 未登录,返回401 response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } // 有token,查询用户信息 try { // 解析成功,证明已经登录 UserInfo user = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey()); // 放入线程域 tl.set(user); return true; } catch (Exception e) { // 抛出异常,证明未登录或超时,返回401 response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } } //afterCompletioncontroller执行结束后执行,释放 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { tl.remove(); } //获取当前线程下的threadlocal变量副本 public static UserInfo getLoginUser() { return tl.get(); } }
配置拦截器 @Configuration @EnableConfigurationProperties(JwtProperties.class) public class MvcConfig implements WebMvcConfigurer { @Autowired private JwtProperties jwtProperties; //注入拦截器到容器 @Bean public LoginInterceptor loginInterceptor() { return new LoginInterceptor(jwtProperties); } 注册拦截器,并添加拦截路径 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor()) .addPathPatterns("/order/**"); } }
service层中获取对应的值 UserInfo user = LoginInterceptor.getLoginUser();