ThreadLocal
·
关于ThreadLocal
背景:学习点评项目——实现登录拦截器/判断用户是否存在
前言
先观看如下代码:
package com.hmdp.utils;
import com.hmdp.dto.UserDTO;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 获取 session
HttpSession session = request.getSession();
// 2. 获取 session 中的用户
Object user = session.getAttribute("user");
// 3. 判断用户是否存在
if (user == null) {
// 4. 不存在,拦截,设置 401 状态码
response.setStatus(401);
return false;
}
// 5. 存在,保存用户信息到 ThreadLocal(便于后续 Service 层获取)
UserHolder.saveUser((UserDTO) user);
// 6. 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求结束后移除用户信息,防止内存泄漏(因为 ThreadLocal 与线程绑定,而 Tomcat 使用线程池)
UserHolder.removeUser();
}
这是一个登录拦截器,用于在处理每次请求前设置ThreadLocal,以及请求完成后移除threadLocal中的用户信息
以下是本篇文章正文内容
一、为什么要将用户信息存入ThreadLocal?
存入ThreadLocal不是多此一举,而是解决了数据传递问题:
- 没有ThreadLocal的情况时,需要在每个方法间传递用户信息
Controller( userDTO ) → Service( userDTO ) → Mapper( userDTO )
- 有ThreadLocal时,任何地方都能直接获取
UserHolder.getUser(); // 任意层、任意方法直接获取
二、为什么在请求处理完成后需要移除ThreadLocal中的用户信息?
核心原因:Tomcat 使用线程池复用线程
用户A请求 → 线程1 → 请求结束,线程1放回池中
用户B请求 → 线程1(被复用)→ 如果没清理,会拿到用户A的信息!
如果不移除,会发生严重的数据串扰问题:
- 用户A登录后,线程1的ThreadLocal中存了用户A的信息
- 用户B未登录,但恰好也被分配到线程1
- 系统错误地认为用户B已经登录了!
三、触发移除的时机
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
UserHolder.removeUser(); // 这里触发!
}
执行时机: 请求处理完成之后(Controller执行完毕、视图渲染完成,完整的请求生命周期如下:
请求进入 → preHandle(存入) → Controller → afterCompletion(移除) → 响应返回
四、为什么不在finally中移除?
可能会问:既然这么重要,为什么不在Controller的finally中移除?
答:拦截器的afterCompletion更可靠 - 即使Controller抛出异常,afterCompletion也一定会执行,避免内存泄漏。
更多推荐

所有评论(0)