在 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 天前
springboot社区养老系统44071-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·php·课程设计
余槐i1 天前
数据没回滚也不报错:@Transactional 自调用失效的 3 种复现与修复
spring boot·mysql·多线程·spring 事务
小蒜学长1 天前
基于SpringBoot的佳新超市管理系统设计与实现系统(代码+数据库+LW)
java·数据库·spring boot·后端·佳新超市管理系统
vx_Biye_Design1 天前
springboot高校选课系统82776-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·课程设计·express
专业程序开发源1 天前
SSM校园拍摄交流服务平台36936-计算机课程设计、毕业设计
java·spring boot·后端·python·elasticsearch·php·课程设计
AI人工智能+电脑小能手1 天前
Java 企业级工具类全家桶-74-JSON序列化工具类
spring boot·jackson·jdk 17·objectmapper·java工具类·json序列化·dto互转
ZealSinger1 天前
Boot4升级PostConstruct不跑了
java·spring boot·spring·升级迁移
专业程序开发源1 天前
django新闻推荐系统70655-计算机课程设计、毕业设计
java·javascript·spring boot·后端·python·django·课程设计
vx_Biye_Design1 天前
springboot一站式旅游管理平台81037-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·课程设计·express·旅游
vx_Biye_Design1 天前
springboot宠物寄养服务预约与监管系统82684-计算机课程设计、毕业设计
java·vue.js·spring boot·elasticsearch·课程设计·express·宠物