springboot项目异步(子线程)处理获取不到header中的token
controller方法中调用service的方法service方法上Async代表异步执行这个方法此时方法中如果获取请求头中的token是获取不到的获取方式如下RequestAttributes requestAttributes RequestContextHolder.getRequestAttributes(); ServletRequestAttributes servletRequestAttributes (ServletRequestAttributes) requestAttributes; HttpServletRequest request servletRequestAttributes.getRequest(); String tokenValue request.getHeader(tokenHeaderKey);具体原因public static RequestAttributes getRequestAttributes() { RequestAttributes attributes (RequestAttributes)requestAttributesHolder.get(); if (attributes null) { attributes (RequestAttributes)inheritableRequestAttributesHolder.get(); } return attributes; } private static final ThreadLocalRequestAttributes requestAttributesHolder new NamedThreadLocal(Request attributes); private static final ThreadLocalRequestAttributes inheritableRequestAttributesHolder new NamedInheritableThreadLocal(Request context);可以看到采用的ThreadLocal存储的request对象所以子线程魂总获取不到主线程的request。解决办法重新创建request对象传参给service中异步方法。首先自定义request对象package com.erbaoge.common.utils; import javax.servlet.*; import javax.servlet.http.*; import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.Principal; import java.util.*; /** * 实现 javax.servlet.http.HttpServletRequest 方法主要用于在子线程中获取参数 * 目前只实现headers 和 parameters 其他属性可自定义 * since 2025/9/15 15:44 */ public class ServletRequestCustom implements HttpServletRequest { private MapString, VectorString headersMap; private MapString, String[] paraMap; public ServletRequestCustom() { headersMap new HashMap(); paraMap new HashMap(); } public ServletRequestCustom(HttpServletRequest request){ this(); setHeaders(request); setParameters(request); } private void setParameters(HttpServletRequest request) { EnumerationString parameterNames request.getParameterNames(); while (parameterNames.hasMoreElements()){ String key parameterNames.nextElement(); paraMap.put(key, request.getParameterValues(key)); } } private void setHeaders(HttpServletRequest request) { EnumerationString headerNames request.getHeaderNames(); while (headerNames.hasMoreElements()) { String key headerNames.nextElement(); EnumerationString headers request.getHeaders(key); VectorString vector new Vector(); while (headers.hasMoreElements()) { vector.add(headers.nextElement()); } headersMap.put(key, vector); } } Override public String getAuthType() { return ; } Override public Cookie[] getCookies() { return new Cookie[0]; } Override public long getDateHeader(String s) { return 0; } Override public String getHeader(String s) { VectorString vector headersMap.get(s); return vector.isEmpty() ? null : vector.get(0); } Override public EnumerationString getHeaders(String s) { return headersMap.get(s.toLowerCase()).elements(); } Override public EnumerationString getHeaderNames() { IteratorString iterator headersMap.keySet().iterator(); return new EnumerationString() { Override public boolean hasMoreElements() { return iterator.hasNext(); } Override public String nextElement() { return iterator.next(); } }; } Override public int getIntHeader(String s) { return 0; } Override public String getMethod() { return ; } Override public String getPathInfo() { return ; } Override public String getPathTranslated() { return ; } Override public String getContextPath() { return ; } Override public String getQueryString() { return ; } Override public String getRemoteUser() { return ; } Override public boolean isUserInRole(String s) { return false; } Override public Principal getUserPrincipal() { return null; } Override public String getRequestedSessionId() { return ; } Override public String getRequestURI() { return ; } Override public StringBuffer getRequestURL() { return null; } Override public String getServletPath() { return ; } Override public HttpSession getSession(boolean b) { return null; } Override public HttpSession getSession() { return null; } Override public String changeSessionId() { return ; } Override public boolean isRequestedSessionIdValid() { return false; } Override public boolean isRequestedSessionIdFromCookie() { return false; } Override public boolean isRequestedSessionIdFromURL() { return false; } Override public boolean isRequestedSessionIdFromUrl() { return false; } Override public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException { return false; } Override public void login(String s, String s1) throws ServletException { } Override public void logout() throws ServletException { } Override public CollectionPart getParts() throws IOException, ServletException { return Collections.emptyList(); } Override public Part getPart(String s) throws IOException, ServletException { return null; } Override public T extends HttpUpgradeHandler T upgrade(ClassT aClass) throws IOException, ServletException { return null; } Override public Object getAttribute(String s) { return null; } Override public EnumerationString getAttributeNames() { return null; } Override public String getCharacterEncoding() { return ; } Override public void setCharacterEncoding(String s) throws UnsupportedEncodingException { } Override public int getContentLength() { return 0; } Override public long getContentLengthLong() { return 0; } Override public String getContentType() { return ; } Override public ServletInputStream getInputStream() throws IOException { return null; } Override public String getParameter(String s) { String[] values paraMap.get(s); return values.length 0 ? values[0] : null; } Override public EnumerationString getParameterNames() { final IteratorString iterator paraMap.keySet().iterator(); return new EnumerationString() { Override public boolean hasMoreElements() { return iterator.hasNext(); } Override public String nextElement() { return iterator.next(); } }; } Override public String[] getParameterValues(String s) { return paraMap.get(s); } Override public MapString, String[] getParameterMap() { return new HashMap(paraMap); } Override public String getProtocol() { return ; } Override public String getScheme() { return ; } Override public String getServerName() { return ; } Override public int getServerPort() { return 0; } Override public BufferedReader getReader() throws IOException { return null; } Override public String getRemoteAddr() { return ; } Override public String getRemoteHost() { return ; } Override public void setAttribute(String s, Object o) { } Override public void removeAttribute(String s) { } Override public Locale getLocale() { return null; } Override public EnumerationLocale getLocales() { return null; } Override public boolean isSecure() { return false; } Override public RequestDispatcher getRequestDispatcher(String s) { return null; } Override public String getRealPath(String s) { return ; } Override public int getRemotePort() { return 0; } Override public String getLocalName() { return ; } Override public String getLocalAddr() { return ; } Override public int getLocalPort() { return 0; } Override public ServletContext getServletContext() { return null; } Override public AsyncContext startAsync() throws IllegalStateException { return null; } Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { return null; } Override public boolean isAsyncStarted() { return false; } Override public boolean isAsyncSupported() { return false; } Override public AsyncContext getAsyncContext() { return null; } Override public DispatcherType getDispatcherType() { return null; } }contoller中方法注入HttpServletRequest对象并创建自定义request对象GetMapping(/test) public Result? test (HttpServletRequest request) { ServletRequestCustom servletRequestCustom new ServletRequestCustom(request); lotteryService.test(servletRequestCustom); return Result.success(); }service中异步方法需要将request放入Async Override public void test(HttpServletRequest request) { RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); try { ...... } finally { RequestContextHolder.resetRequestAttributes(); } }上述方法是spring的异步执行方法方法中就能成功使用开始的那种方式获取到token。两点疑问为什么不直接传参从controller中注入的request对象为什么不在controller线程中获取ServletRequestAttributes后再放入子线程获取参数传参true。使用代码RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request), true);答案是以上两种方式都不能成功在异步方法中获取token详细原因还未深究。类似文章【问题】spring boot中是Java8 parallelStream 造成的RequestContextHolder.getRequestAttributes()为null-CSDN博客

相关新闻

数中实战:3个完整示例搞定复杂数据结构

数中实战:3个完整示例搞定复杂数据结构

数中实战:3个完整示例搞定复杂数据结构 看到满屏红色的 StackTrace,心里是不是发慌?报错信息像天书,根本不知道从哪下手调试。别急,今天不聊虚的,直接上干货。…

2026/9/23 16:28:32 阅读更多 →
壁纸下载免费壁纸源码拆解:搞定高频面试题里的并发陷阱

壁纸下载免费壁纸源码拆解:搞定高频面试题里的并发陷阱

壁纸下载免费壁纸源码拆解:搞定高频面试题里的并发陷阱 复制来的代码跑不通不知道怎么调,这种绝望感每个后端老手都懂。你盯着满屏的报错,心想这明明是个简单的壁纸下载功能,怎么一上量就崩?更扎心的是,面试时被问到“如何保证高并发下的文件完整性”,…

2026/9/22 11:23:58 阅读更多 →
配置环境卡半天?一文搞懂一折网底层原理

配置环境卡半天?一文搞懂一折网底层原理

配置环境卡半天?一文搞懂一折网底层原理 是不是每次遇到“一折网”这种网络协议相关的概念,配置环境就卡半天?明明照着教程敲代码,结果就是连不上,抓包看半天全是乱码。别急,今天咱们不整虚的, 一文搞懂…

2026/9/22 11:23:58 阅读更多 →

最新新闻

机器人在认知症非药物干预中的证据现状:循证综述

机器人在认知症非药物干预中的证据现状:循证综述

摘要认知症的行为与心理症状(BPSD)管理日益强调非药物干预优先。机器人辅助疗法作为宠物辅助疗法的技术化延伸,在近十年积累了从随机对照试验到案例报告的多元证据。本文基于现有系统综述、荟萃分析与单项研究,对机器人辅助疗法在…

2026/9/23 16:28:26 阅读更多 →
光荣岁月下载实战:3个方案完整示例与避坑指南

光荣岁月下载实战:3个方案完整示例与避坑指南

光荣岁月下载实战:3个方案完整示例与避坑指南 刚把项目跑起来,控制台直接红屏?StackTrace 长得像天书, NullPointerException 混着 IOError…

2026/9/23 16:28:26 阅读更多 →
Ontology(本体)怎样工作?RDF、OWL、SPARQL、SHACL 各管什么

Ontology(本体)怎样工作?RDF、OWL、SPARQL、SHACL 各管什么

上面这张图,先把本文要讲的事说完了。 同一张售后工单,会依次遇到四类问题:事实怎么表达,规则怎么推理,结果怎么查出来,当前数据够不够进入下一步。很多 Ontology 文章会从 RDF、OWL、SPARQL、SHACL 的定义…

2026/9/23 16:28:26 阅读更多 →
EMQX 5.x TCP 连接拥塞告警(conn_congestion)默认关闭:配置详解与源码实现

EMQX 5.x TCP 连接拥塞告警(conn_congestion)默认关闭:配置详解与源码实现

后端物联网消息队列通信 【免费下载链接】emqx The most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles 项目地址: https://gitcode.com/gh_mirrors/em/emqx 点击查看 免费下载 本文基于当前仓库 changes/ee/fix-16725.en.md 的变更…

2026/9/23 16:28:26 阅读更多 →
五险一金扣多少钱全解析附完整示例避坑指南

五险一金扣多少钱全解析附完整示例避坑指南

五险一金扣多少钱全解析附完整示例避坑指南 配置环境就卡半天,算薪单又对不上,五险一金扣多少钱成了职场人最头疼的谜题。别急,这篇给你一套完整示例,从社保基数到公积金比例,把扣款逻辑拆得明明白白,让你一眼看懂工资条上的每一个数字。…

2026/9/23 16:28:26 阅读更多 →
Java Swing+MySQL员工工资管理系统:课程设计实战与排错指南

Java Swing+MySQL员工工资管理系统:课程设计实战与排错指南

简介:面向Java初学者的员工工资管理系统,采用Java Swing搭建桌面界面、MySQL负责数据持久化,实现了管理员与普通用户双角色体系,覆盖员工信息增删改查、部门维护、工资标准设置、工资查询与统计等业务模块,适合作为课程…

2026/9/23 16:27:25 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →