在 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();
    }
}
相关推荐
深漂的华哥1 小时前
Ruoyi-Plus前后端分离场景下,数据加密传输
java·spring boot·后端·开源·maven·ruoyi
剑胆琴心静水深流3 小时前
全栈之路6---web集成与呈现
前端·vue.js·spring boot·分布式·spring·前端框架·npm
qq_185198696 小时前
SpringBoot-五-AOT
java·spring boot·后端
卓怡学长7 小时前
w180springboot基于Java的悠扬乐器管理
java·spring boot·mysql·spring·maven·intellij-idea
choumou_M8 小时前
SpringBoot_5:商品秒杀场景的并发实践(初步)
java·spring boot·后端
攻城有术9 小时前
专项攻克-SpringBoot启动后 Bean的实例数量问题
java·spring boot·后端
程序员贺加贝10 小时前
别把 ThreadLocal 写进 Serializer:一次 SaaS 多租户 Jackson 上下文污染排查
java·spring boot
evans在进步10 小时前
Spring Boot 工程化核心详解:Parent、Starter、热部署、事务与多数据源
spring boot·后端·python
卓怡学长10 小时前
w181springboot机场乘客服务系统
java·数据库·spring boot·spring·intellij-idea
罗超驿12 小时前
SpringBoot 快速入门
java·spring boot·后端