在 spring boot 项目中一般如何存储上下文数据?

答:可以使用 ThreadLocal 在每个线程的上下文中存储数据,从而在同一线程内的任何地方对这些数据进行访问。

使用 ThreadLocal 可以确保每个线程都拥有独立的 userId 存储,避免了多线程环境下的并发问题。这种方法非常适合在整个请求生命周期中需要访问和使用 userId 的情况。

userId 为例,假如我们的 cookie 中包含 userId 信息。

当然一般的项目中 cookie 中仅存储用于鉴权的 token 令牌信息,不会直接存储 userId

1、创建一个 ThreadLocal 类

首先,创建一个 ThreadLocal 存储类,用于存储 userId

java 复制代码
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

java 复制代码
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、配置拦截器

创建一个配置类来注册拦截器。

java 复制代码
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

java 复制代码
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 创建一个切面。

java 复制代码
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();
    }
}
相关推荐
zdl68615 分钟前
SpringBoot Test详解
spring boot·后端·log4j
拾荒的小海螺32 分钟前
JAVA:Spring Boot3 集成 Spring AI + Ollama 本地模型
java·spring boot·spring
sxhcwgcy33 分钟前
IDEA+Docker一键部署项目SpringBoot项目
spring boot·docker·intellij-idea
未来转换1 小时前
Java 21 + Spring Boot + Elasticsearch 8.10 实现语义搜索
java·开发语言·spring boot·后端·elasticsearch·ai·图搜索算法
程序员小蛋1 小时前
基于springboot宠物咖啡馆平台的设计与实现.7z(源码+论文)
spring boot·后端·宠物
爱码少年1 小时前
Springboot 工程中快速判断web应用服务器类型
java·spring boot
禾小西1 小时前
Knife4j 快速入门:集Swagger2 和 OpenAPI3 为一体的增强解决方案
java·spring boot·后端
polaris06301 小时前
Docker部署Spring Boot + Vue项目
vue.js·spring boot·docker
weixin_704266051 小时前
Spring Boot (整合 Mybatis + 自动配置原理)
spring boot·笔记·mybatis