部署
a
TreadLocal保存当前用户信息
1.1 ThreadLocal概述
- ThreadLocal是Java中用于实现线程局部变量的工具类,它为每个线程提供独立的变量副本,解决多线程环境下的数据隔离问题。其核心原理是通过ThreadLocalMap存储线程专属数据,避免同步开销,适用于数据库连接管理、用户会话存储等场景,但需注意内存泄露风险
- ThreadLocal和当前线程绑定到一起,当前线程结束了,ThreadLocal就没有了
- ThreadLoca没有了具体指的是 当前对象 没有了,值还存在
- map.set(this, value); this 没有了,value还在内存中
- 手动清除value值,remove方法,不然造成内存泄露
1.2 使用场景
接口:获取登录用户个人信息
登录成功后,跳转到主页面中,在主页面里面调用接口,获取当前登录成功的用户信息(用户名或者头像等)
分析过程
- 浏览器发送请求,首先到拦截器,拦截器把请求拦下
- 在拦截器校验是否登录,如果是登录,把请求放行
- 放行之后,执行后面接口,接口返回数据
- 这样完成一次请求过程
- 简单理解为一次请求开启线程
- 使用ThreadLocal和当前线程绑定到一起
具体实现
- 创建ThreadLocal工具类,封装向ThreaLocal放数据,取数据和清除数据的方法
1
2
3
4
5
6
7
8
public class LoginUser {
private Long userId;
private String username;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class LoginUserContext {
private static final ThreadLocal<LoginUser> userThreadLocal = new ThreadLocal<>();
// 存放
public static void setLoginUser(LoginUser loginUser) {
userThreadLocal.set(loginUser);
}
// 获取
public static LoginUser getLoginUser() {
return userThreadLocal.get();
}
// 清除
public static void clear() {
userThreadLocal.remove();
}
}在拦截器方法里面使用ThreadLocal,向ThreadLocal中放用户数据
在接口中,直接从ThreadLocal获取拦截器时放数据
Token 只在拦截器解析一次,然后通过
ThreadLocal保存当前登录用户,Controller 和 Service 直接获取,无需重复解析 JWT。- 在拦截器方法中使用、threadLocal,向ThreadLocal里存放用户数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class AuthenticationInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("access-token");
//String token = "aaaaabc";
//String token = "eyJhbGciOiJIUzI1NiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAA_6tWKi5NUrJSCg12DYr39HPzV9JRSq0oULIyNLcwMTUxNjY10lEqLU4t8kxRsrI0hLDzEnNTgXpySpMrlWoBQVhMh0IAAAA.ZEWIGVl1GrcNMOxwIgq_2A4jPC6ktM8VgGFFXVtwaEI";
// 判断token是否为空
if (token == null) {
throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);
} else {
// 如果不为空,解析token
Claims claims = JwtUtil.parseToken(token);
Long userId = claims.get("userId", Long.class);
String username = claims.get("username", String.class);
System.out.println("拦截器解析token获取userId = " + userId);
// 解析token成功之后,根据userId查询数据库,判断用户是否存在
// 把userId放到ThreadLocal中
LoginUser loginUser = new LoginUser();
loginUser.setUserId(userId);
loginUser.setUsername(username);
LoginUserContext.setLoginUser(loginUser);
}
return true;
}
// 清除ThreadLocal值,防止内存泄露
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
LoginUserContext.clear();
}
}- 在接口里面,直接从ThreadLocal获取拦截器时存放数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public Result<UserInfoVo> info(HttpServletRequest request) {
// 1. 从请求头获取当前登录的用户id
// 用户id在请求头token中
//String token = request.getHeader("access-token");
// 解析token 使用jwt
//Claims claims = JwtUtil.parseToken(token);
//Long userId = claims.get("userId", Long.class);
// 直接从ThreadLocal获取userId
LoginUser loginUser = LoginUserContext.getLoginUser();
Long userId = loginUser.getUserId();
// 2. 根据用户id查询数据库表,获取用户信息
UserInfoVo userInfoVo = loginService.getUserInfoById(userId);
return Result.ok();
}测试:

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 𝐺ℎ𝑎𝑜 の 𝐵𝑙𝑜𝑔!
评论
