首页 > 其他分享 >SpringBoot--解决子线程无法获得HttpServletRequest的attribute的问题

SpringBoot--解决子线程无法获得HttpServletRequest的attribute的问题

时间:2022-10-03 11:03:14浏览次数:71  
标签:HttpServletRequest SpringBoot -- attribute request 线程 servletRequestAttributes i


简介

        本文介绍解决SpringBoot子线程无法获得HttpServletRequest的attribute的问题。

        在SpringBoot请求中,如果创建了子线程获取request的attribute,会无法获取到。比如:我想记录日志,将日志放到了request的attribute中:request.setAttribute("logContent", "日志内容"),然后创建子线程去获取logContent,然后进行处理。

问题复现

代码

package com.knife.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@RestController
public class HelloController {

@GetMapping("/test")
public String test() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
request.setAttribute("logContent", "日志内容:" + LocalDateTime.now());

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();

System.out.println("子线程运行。获取到的attribute为:"
+ request.getAttribute("logContent"));
}
});
thread.start();

return "test success";
}
}

结果(getRequestAttributes返回null)

Exception in thread "Thread-131" java.lang.NullPointerException
at com.knife.controller.HelloController$1.run(HelloController.java:26)
at java.lang.Thread.run(Thread.java:748)

解决方案

创建子线程之前调用如下代码:

RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);

代码

package com.knife.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@RestController
public class HelloController {

@GetMapping("/test")
public String test() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
request.setAttribute("logContent", "日志内容:" + LocalDateTime.now());

RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();

System.out.println("子线程运行。获取到的attribute为:"
+ request.getAttribute("logContent"));
}
});
thread.start();

return "test success";
}
}

结果(可以获取到属性)

子线程运行。获取到的attribute为:日志内容:2022-09-03T16:43:18.463

线程安全性

        上边的解决方案是否有线程安全问题呢?我在子线程里延时5秒模拟长时间请求,然后多次请求,看结果。如果值都是不一样的话,应该是线程安全的。(本处我无法百分百确定,实际开发中不太建议这么写。实际开发中建议将日志内容作为线程的参数传入,与attribute剥离开)。

代码

package com.knife.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@RestController
public class HelloController {

@GetMapping("/test")
public String test() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
request.setAttribute("logContent", "日志内容:" + LocalDateTime.now());

RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();

System.out.println("子线程运行。获取到的attribute为:"
+ request.getAttribute("logContent"));

try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();

return "test success";
}
}

结果(日志内容是新的,应该是线程安全的)

子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:42.242
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:42.799
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:43.359
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:43.877
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:44.392
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:44.804
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:45.247
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:45.610
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:46.010
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:46.433

其他网址

​RequestContextHolder.getRequestAttributes()子线程获取问题_ypp91zr的博客-​

​spring boot开启异步导致RequestContextHolder.currentRequestAttributes()为NULL_ERD Online的博客​


标签:HttpServletRequest,SpringBoot,--,attribute,request,线程,servletRequestAttributes,i
From: https://blog.51cto.com/knifeedge/5729942

相关文章

  • SpringBoot--手动校验@NotBlank、@NotNull等的工具类
    ​简介说明        本文介绍手动校验@NotBlank、@NotNull等的工具类。    使用场景:controller接口的入参很多,需要他人提供,但他人提供的类的字段很多,字段都......
  • SpringBoot--解决@Transactional与@CacheEvict联合使用导致的缓存与数据库的一致性问
    ​简介说明    本文介绍@Transactional与@CacheEvict联合使用导致的缓存与数据库的一致性问题的原因及解决方案。注解的作用        @Transactional:给当前......
  • 智慧农业系统 - 可视化大屏(Echarts)&管理系统(HTTP(S)协议)&物联网平台(MQTT协议)
    一、平台功能特点农业数据实时监控,实时视频监控,历史数据分析;支持电子地图,设备地理位置精确定位;支持多级组织结构管理,满足集团大客户需求;可视化大屏展示,数据指标一目了然,彰显......
  • 玩转 Flowable 流程实例
    文章目录​​1.捋清三个概念​​​​2.五种流程启动方式​​​​3.简单实践​​​​4.删除流程实例​​​​5.获取运行的活动节点​​​​上篇文章​​松哥和大家聊了......
  • Go的Struct结构体和(Json Form tag)
    Go的Struct结构体和(JsonFormtag)1.Struct1.1定义使用struct关键字可以定义一个结构体,结构体中的成员,称为结构体的字段或属性。typeMemberstruct{idin......
  • 视图集
    两个视图基类1.drf提供了一个顶层的视图APIView,可以通过继承APIView写视图,之后我们写的代码可能重复代码比较多,就可以使用面向对象的继承、封装,GenericAPIView继承了APIVi......
  • Docker中实现macvlan的穿透访问
    需求因为性能等原因,我们需要用macvlan方式部署container而kernel中有关macvlan的安全策略会完全过滤来自host访问host想要访问container服务端口很不方便。破解实现ho......
  • 洛谷 P3388 【模板】割点(割顶)
    题目链接:https://www.luogu.com.cn/problem/P3388 【模板】割点(割顶)题目背景割点题目描述给出一个$n$个点,$m$条边的无向图,求图的割点。输入格式第一行输入两个......
  • 人如蝼蚁,跌倒再起。
    ......
  • 10.2模拟赛总结
    模拟赛总结T1预计得分:100实际得分:30挂分原因:式子推错了没开longlongT2预计得分:80实际得分:80T3预计得分:50实际得分:10挂分原因:单调栈右半部分写错了T4......