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

相关推荐
2201_75783087几秒前
条件分页查询
java·开发语言
LYFlied3 分钟前
【每日算法】LeetCode 105. 从前序与中序遍历序列构造二叉树
数据结构·算法·leetcode·面试·职场和发展
重生之我是Java开发战士4 分钟前
【数据结构】Java对象的比较
java·jvm·数据结构
DanyHope8 分钟前
LeetCode 206. 反转链表:迭代 + 递归双解法全解析
算法·leetcode·链表·递归·迭代
叮咚侠8 分钟前
Ubuntu 24.04.3 LTS如何扩容逻辑卷
linux·数据库·ubuntu
橘子1311 分钟前
Linux线程——一些概念(七)
java·redis·缓存
NAGNIP12 分钟前
才发现TensorBoard是个可视化的神器!
算法
magic_kid_201015 分钟前
IDEA 复制到 Windows 远程桌面失败的原因与解决方案
java·ide·intellij-idea
张人玉15 分钟前
c#DataTable类
数据库·c#
风月歌16 分钟前
基于微信小程序的学习资料销售平台源代码(源码+文档+数据库)
java·数据库·mysql·微信小程序·小程序·毕业设计·源码