父子线程间传值问题以及在子线程或者异步情况下使用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博客

相关推荐
Otaku love travel4 分钟前
老系统改造增加初始化,自动化数据源配置(tomcat+jsp+springmvc)
java·tomcat·初始化·动态数据源
九皇叔叔17 分钟前
【9】PostgreSQL 之 vacuum 死元组清理
数据库·postgresql
DKPT18 分钟前
Java设计模式之行为型模式(责任链模式)介绍与说明
java·笔记·学习·观察者模式·设计模式
L_autinue_Star33 分钟前
手写vector容器:C++模板实战指南(从0到1掌握泛型编程)
java·c语言·开发语言·c++·学习·stl
我爱C编程38 分钟前
基于Qlearning强化学习的1DoF机械臂运动控制系统matlab仿真
算法
风雅的远行者38 分钟前
mysql互为主从失效,重新同步
数据库·mysql
晨岳1 小时前
CentOS 安装 JDK+ NGINX+ Tomcat + Redis + MySQL搭建项目环境
java·redis·mysql·nginx·centos·tomcat
chao_7891 小时前
CSS表达式——下篇【selenium】
css·python·selenium·算法
执笔诉情殇〆1 小时前
前后端分离(java) 和 Nginx在服务器上的完整部署方案(redis、minio)
java·服务器·redis·nginx·minio
chao_7891 小时前
Selenium 自动化实战技巧【selenium】
自动化测试·selenium·算法·自动化