1.ThreadLocal存储用户信息
ThreadLocal简介
ThreadLocal 是 Java 中一个非常有用的工具,尤其在多线程环境下,使用它可以为每个线程独立存储变量,而不会与其他线程共享。
这在存储用户信息时非常方便,比如在 web 应用程序中,每个请求可能对应一个独立的线程,使用 ThreadLocal 可以在每个请求线程中存储和获取用户信息。
ThreadLocal 最常见的应用场景是需要隔离线程间的数据共享,确保每个线程拥有自己独立的变量副本。它适用于线程生命周期较长,且需要在多个方法或组件间共享特定数据的场景,但需小心管理,以避免潜在的内存泄漏问题。
示例1:如何使用 ThreadLocal 存储和获取用户信息
public class UserContext {
// 创建 ThreadLocal 变量,用于存储每个线程独立的用户信息
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
// 设置当前线程的用户信息
public static void setUser(User user) {
userThreadLocal.set(user);
}
// 获取当前线程的用户信息
public static User getUser() {
return userThreadLocal.get();
}
// 清除当前线程的用户信息(防止内存泄漏)
public static void clear() {
userThreadLocal.remove();
}
}
// 假设有一个 User 类表示用户信息
class User {
private String username;
private String email;
public User(String username, String email) {
this.username = username;
this.email = email;
}
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
}
UserContext 是一个自定义的类,用于管理和存储与用户相关的信息。
在上面的示例中,UserContext 封装了对 ThreadLocal 变量的管理,以便为每个线程单独存储和访问用户信息。它提供了 setUser、getUser 和 clear 三个静态方法:
setUser(User user):将用户信息存储到当前线程的ThreadLocal中。getUser():从当前线程的ThreadLocal中获取用户信息。clear():从当前线程的ThreadLocal中移除用户信息,防止内存泄漏。
在业务逻辑中,可以通过以下方式设置、获取和清除用 户信息:
public class UserService {
public void processRequest(String username, String email) {
// 模拟设置用户信息
User user = new User(username, email);
UserContext.setUser(user);
// 获取用户信息
User currentUser = UserContext.getUser();
System.out.println("Current User: " + currentUser.getUsername());
// 处理完毕后清除用户信息
UserContext.clear();
}
}
在 Web 应用中,可以在请求进入时设置用户信息(例如在过滤器或拦截器中),并在请求完成后进行清理。
示例2:ThreadLocal结合拦截器使用
可以使用拦截器(Interceptor)来结合 UserContext 实现用户信息的自动注入和清理。
-
创建
UserContext类使用
UserContext将用户信息与当前线程关联。public class UserContext {
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
public static void setUser(User user) {
userThreadLocal.set(user);
}
public static User getUser() {
return userThreadLocal.get();
}
public static void clear() {
userThreadLocal.remove();
}
} -
创建
UserInterceptor拦截器在拦截器中,可以在请求处理前设置用户信息,处理完后进行清理。
在
preHandle方法中,拦截器从请求头中提取用户信息并存储到UserContext中。afterCompletion方法则在请求完成后清理ThreadLocal,以避免内存泄漏。import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 假设从请求头中获取用户名和邮箱
String username = request.getHeader("username");
String email = request.getHeader("email");
if (username != null && email != null) {
// 创建用户对象并设置到 UserContext
User user = new User(username, email);
UserContext.setUser(user);
}
return true; // 返回 true 以继续处理请求
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求处理完成后,清理 ThreadLocal 中的用户信息
UserContext.clear();
}
} -
注册拦截器
在 Spring 的配置类中注册
UserInterceptor,以使其生效。UserInterceptor会在每次请求进入控制器之前设置用户信息,并在请求完成后清理。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 {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInterceptor()).addPathPatterns("/**");
}
} -
在业务逻辑中获取用户信息
通过
UserContext.getUser()获取当前请求的用户信息。每个请求的用户信息都是线程安全的,不会被其他请求影响。public class UserService {
public void process() {
User currentUser = UserContext.getUser();
if (currentUser != null) {
System.out.println("Processing for user: " + currentUser.getUsername());
} else {
System.out.println("No user information available.");
}
}
}