用到的工具类:
@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();
}
}
}
第一种情况(子线程无法获取到父线程的值):
@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();
}
结果:
解决办法:
@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);开启父子线程间通信
第二中情况(主线程结束,子线程无法获取到值):
@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.使用
依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.6.0</version>
</dependency>
TransmittableThreadLocal上下文工具类
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();
}
}
}
拦截器
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();
}
}
注册:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/register");
}
}
使用:
@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博客