父子线程间传值问题以及在子线程或者异步情况下使用RequestContextHolder.getRequestAttributes()的注意事项和解决办法

用到的工具类:

java 复制代码
@Slf4j
@Configuration
@Lazy(false)
public class SpringContextUtil{
  public static HttpServletRequest getRequest() {
    ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    if (servletRequestAttributes == null) {
        throw new RuntimeException("无法获取servletRequestAttributes ");
    } else {
        return servletRequestAttributes.getRequest();
    }
 }

}

 

第一种情况(子线程无法获取到父线程的值):

java 复制代码
    @GetMapping("/threadValueTransfer1")
    private void ThreadValueTransfer1(){
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),SpringContextUtil.getRequest().getHeader("age"));
        new Thread(()->{
            String age = SpringContextUtil.getRequest().getHeader("age");
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

结果:

解决办法:

java 复制代码
  @GetMapping("/threadValueTransfer2")
    private void ThreadValueTransfer2() throws InterruptedException {
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),SpringContextUtil.getRequest().getHeader("age"));
        RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
        new Thread(()->{
            String age = SpringContextUtil.getRequest().getHeader("age");
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

使用RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);开启父子线程间通信

第二中情况(主线程结束,子线程无法获取到值):

java 复制代码
  @GetMapping("/threadValueTransfer3")
    private void threadValueTransfer3() throws InterruptedException {
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),SpringContextUtil.getRequest().getHeader("age"));
        RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
        new Thread(()->{
            try {
                Thread.sleep(3*1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            String age = SpringContextUtil.getRequest().getHeader("age");
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

结果:

解决(使用阿里的ttl)

步骤:

1.拦截器拦截将请求头中信息放到TransmittableThreadLocal中

2.注册拦截器

3.使用

依赖:

java 复制代码
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.6.0</version>
        </dependency>

TransmittableThreadLocal上下文工具类

java 复制代码
public class AgeContext {

    private static final ThreadLocal<String> AGE = new TransmittableThreadLocal<>();

    public static String get() {
        return AGE.get();
    }

    public static void set(String age) {
        AGE.set(age);
    }

    public static void clean() {
        if (AGE.get() != null) {
            AGE.remove();
        }
    }

}

拦截器

java 复制代码
public class RequestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String age = request.getHeader("age");
        AgeContext.set(age);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AgeContext.clean();
    }
}

注册:

java 复制代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/register");
    }
}

使用:

java 复制代码
 @GetMapping("/threadValueTransfer4")
    private void threadValueTransfer4() throws InterruptedException {
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),AgeContext.get());
        new Thread(()->{
            try {
                Thread.sleep(3*1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            String age = AgeContext.get();
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

结果:

注意:

阿里的ttl用法就是ThreadLocal的用法,具体参照:积累知识库:ThreadLocal在工作中是怎么使用_analysiscontext threadlocal-CSDN博客

相关推荐
oak隔壁找我1 小时前
JVM常用调优参数
java·后端
蝎子莱莱爱打怪5 小时前
OpenClaw 从零配置指南:接入飞书 + 常用命令 + 原理图解
java·后端·ai编程
NineData5 小时前
NineData智能数据管理平台新功能发布|2026年1-2月
数据库·sql·数据分析
IvorySQL6 小时前
双星闪耀温哥华:IvorySQL 社区两项议题入选 PGConf.dev 2026
数据库·postgresql·开源
狼爷7 小时前
Go 没有 override?别硬套继承!用接口+嵌入,写更清爽的“覆盖”逻辑
java·go
会员源码网8 小时前
使用`mysql_*`废弃函数(PHP7+完全移除,导致代码无法运行)
后端·算法
木心月转码ing9 小时前
Hot100-Day10-T438T438找到字符串中所有字母异位词
算法
小兔崽子去哪了9 小时前
Java 自动化部署
java·后端
ma_king9 小时前
入门 java 和 数据库
java·数据库·后端
后端AI实验室9 小时前
我用Cursor开发了3个月,整理出这套提效4倍的工作流
java·ai