在 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();
    }
}
相关推荐
用户908324602738 小时前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840821 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解1 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解1 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记1 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840822 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解2 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者3 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺3 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端