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

相关推荐
0xDevNull4 分钟前
Java泛型详解
java·开发语言·后端
嘻嘻哈哈樱桃5 分钟前
牛客经典101题解题集--贪心算法+模拟
java·python·算法·贪心算法
AKDreamer_HeXY6 分钟前
QOJ 12255 - 36 Puzzle 题解
数据结构·c++·数学·算法·icpc·qoj
七夜zippoe8 分钟前
DolphinDB分区策略:VALUE分区详解
数据库·oracle·分区·value·dolphindb
rKWP8gKv79 分钟前
数据库连接池选型:HikariCP与Druid的性能对比
数据库
AI进化营-智能译站14 分钟前
ROS2 C++开发系列13-运算符重载让ROS2消息处理更自然
java·开发语言·c++·ai
AI科技星14 分钟前
《全域数学》第一部 数术本源 第三卷 代数原本第14篇 附录二 猜想证明【乖乖数学】
人工智能·算法·数学建模·数据挖掘·量子计算
dreamZhanglx15 分钟前
MySQL进阶
数据库·mysql
有浔则灵16 分钟前
GORM 日志与调试完全指南:从基础配置到生产实践
服务器·数据库·gorm
xmjd msup16 分钟前
MySQL 函数
数据库·mysql