答:可以使用 ThreadLocal
在每个线程的上下文中存储数据,从而在同一线程内的任何地方对这些数据进行访问。
使用 ThreadLocal 可以确保每个线程都拥有独立的 userId 存储,避免了多线程环境下的并发问题。这种方法非常适合在整个请求生命周期中需要访问和使用 userId 的情况。
以 userId
为例,假如我们的 cookie 中包含 userId
信息。
当然一般的项目中 cookie 中仅存储用于鉴权的 token 令牌信息,不会直接存储
userId
。
1、创建一个 ThreadLocal 类
首先,创建一个 ThreadLocal
存储类,用于存储 userId
。
public class UserContext {
private static final ThreadLocal<String> userIdThreadLocal = new ThreadLocal();
public static void setUserId(String userId) {
userIdThreadLocal.set(userId);
}
public static String getUserId() {
return userIdThreadLocal.get();
}
public static void clear() {
userIdThreadLocal.remove();
}
}
2、创建一个拦截器
创建一个拦截器,在请求处理之前解析 Cookie
并设置 userId
, 在请求处理完成后清理 ThreadLocal
。
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class UserIdInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = null;
// 从请求中获取 Cookies
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("userId".equals(cookie.getName())) {
userId = cookie.getValue();
break;
}
}
}
// 如果找到了 userId,将其存储到 ThreadLocal 中
if (userId != null) {
UserContext.setUserId(userId);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清理 ThreadLocal
UserContext.clear();
}
}
3、配置拦截器
创建一个配置类来注册拦截器。
代码语言:javascript复制import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private UserIdInterceptor userIdInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userIdInterceptor)
.addPathPatterns("/**"); // 拦截所有请求
}
}
4、在需要使用 userId 的地方获取 userId
在需要使用 userId 的地方,通过 UserContext
获取 userId
。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/userinfo")
public String getUserInfo() {
// 从 UserContext 中获取 userId
String userId = UserContext.getUserId();
return "User ID is: " userId;
}
}
5、使用 AOP (可选)
如果需要在整个应用程序中自动处理 ThreadLocal
的设置和清理,可以使用 Spring AOP 创建一个切面。
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UserIdAspect {
@Before("execution(* com.example..*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void beforeController() {
// 从上下文中获取请求和用户信息
// 设置 UserContext 中的 userId
}
@After("execution(* com.example..*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void afterController() {
// 清理 UserContext 中的 userId
UserContext.clear();
}
}