SpringBoot异步任务获取HttpServletRequest

前言

在使用框架日常开发中需要在controller中进行一些异步操作减少请求时间,但是发现在使用@Anysc注解后会出现Request对象无法获取的情况,本文就此情况给出完整的解决方案

原因分析

@Anysc注解会开启一个新的线程,主线程的Request和子线程是不共享的,所以获取为null

在使用springboot的自定带的线程共享后,代码如下,Request不为null,但是偶发的其中body/head/urlparam内容出现获取不到的情况,是因为异步任务在未执行完毕的情况下,主线程已经返回,拷贝共享的Request对象数据被清空

ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//设置子线程共享
RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);
HttpServletRequest request = servletRequestAttributes.getRequest();

解决方案


前置条件
        启动类添加@EnableAsync注解
        标记@Async的异步方法不能和调用者在同一个class中

pom配置

<!-- 阿里线程共享 --><dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.11.0</version></dependency>

requrest共享
通过TransmittableThreadLocal对象进行线程对象共享

public class CommonUtil {public static TransmittableThreadLocal<HttpServletRequest> requestTransmittableThreadLocal = new TransmittableThreadLocal<HttpServletRequest>();public static void shareRequest(HttpServletRequest request){requestTransmittableThreadLocal.set(request);}public static HttpServletRequest getRequest(){HttpServletRequest request = requestTransmittableThreadLocal.get();if(request!=null){return requestTransmittableThreadLocal.get();}else{ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if(requestAttributes!=null){return  requestAttributes.getRequest();}else{return  null;}}}public static void remove(){requestTransmittableThreadLocal.remove();}
}

:系统中所有Request获取需要统一从CommonUtil指定来源,例如token鉴权等

自定义request过滤器

通过自定义过滤器对Request的内容进行备份保存,主线程结束时Request清除结束不会影响到子线程的相应参数的获取,也适用于增加拦截器/过滤器后body参数无法重复获取的问题。需要注意的是对header参数处理时key要忽略大小写
public class HttpServletRequestReplacedFilter implements Filter, Ordered {@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {ServletRequest requestWrapper = null;if (request instanceof HttpServletRequest) {requestWrapper = new RequestWrapper((HttpServletRequest) request);}//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。// 在chain.doFiler方法中传递新的request对象if (requestWrapper == null) {chain.doFilter(request, response);} else {chain.doFilter(requestWrapper, response);}}@Overridepublic void init(FilterConfig arg0) throws ServletException {}@Overridepublic int getOrder() {return 10;}
}public class RequestWrapper extends HttpServletRequestWrapper{private final byte[] body;private final HashMap<String,String> headMap;private final HashMap<String,String> requestParamMap;public RequestWrapper(HttpServletRequest request) throws IOException {super(request);body = CommonUtil.getBodyString(request).getBytes(Charset.forName("UTF-8"));headMap = new HashMap();Enumeration<String> headNameList = request.getHeaderNames();while (headNameList.hasMoreElements()){String key = headNameList.nextElement();headMap.put(key.toLowerCase(),request.getHeader(key));}requestParamMap = new HashMap<>();Enumeration<String> parameterNameList = request.getParameterNames();while (parameterNameList.hasMoreElements()){String key = parameterNameList.nextElement();requestParamMap.put(key,request.getParameter(key));}}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}@Overridepublic String getHeader(String name) {return headMap.get(name.toLowerCase());}@Overridepublic String getParameter(String name) {return requestParamMap.get(name);}
}

自定义任务执行器


用于拦截异步任务执行,在任务执前统一进行Request共享操作,且可以定义多个,不影响原有的异步任务代码

public class CustomTaskDecorator implements TaskDecorator {@Overridepublic Runnable decorate(Runnable runnable) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();System.out.println("异步任务共享request");return () -> {try {CommonUtil.shareRequest(request);runnable.run();} finally {CommonUtil.remove();}};}
}@Configuration
public class TaskExecutorConfig {@Bean()public Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(200);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("taskExecutor-");executor.setAwaitTerminationSeconds(60);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}@Bean("shareTaskExecutor")public Executor hpTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(200);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("shareTaskExecutor-");executor.setWaitForTasksToCompleteOnShutdown(true);executor.setAwaitTerminationSeconds(60);// 增加 TaskDecorator 属性的配置executor.setTaskDecorator(new CustomTaskDecorator());executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}

调用示例

给@Anysc注解指定进行共享拦截的任务执行器即可

@PostMapping("/testAsync")@ResponseBodypublic Object testAsync(@RequestBody Map<String, Object> params) throws Exception{Result result = Result.okResult();asyncUtil.executeAsync();return result;}@Component
public class AsyncUtil {@Async("shareTaskExecutor")public void executeAsync () throws InterruptedException {System.out.println("开始执行executeAsync");Thread.sleep(3000);System.out.println("结束执行executeAsync");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/694426.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot的约定优于配置,你的理解是什么?

我从4个点方面来回答。 首先&#xff0c;约定优于配置是一种软件设计的范式&#xff0c;它的核心思想是减少软件开发人员对于配置项的维护&#xff0c;从而让开发人员更加聚焦在业务逻辑上。 SpringBoot就是约定优于配置这一理念下的产物&#xff0c;它类似于Spring框架下的一…

Vue3-组合式Api(重点)

阅读文章你可以收获的知识 1.知道setup语法糖的使用和如何实现的 2.知道在vue3中如何定义响应式数据 3.知道在vue3中如何定义一个计算属性&#xff08;computed&#xff09; 4.知道如何在vue3中使用watch来监听数据 5.知道在vue3如何实现父子通信 6.知道vue3如何使用ref函…

BIM工程师认证相关信息

目录 背景 一、中国图学学会颁发的全国BIM技能等级考试证书 二、中国建设教育协会颁发的BIM证书 背景 BIM工程师证书是建筑信息模型证书&#xff0c;英文全称是Building Information Modeling。对于建筑工程从业人员来说&#xff0c;BIM证书可以证明个人的工作能力&#xf…

软件测试工程师linux学习之系统层面相关命令总结

1 linux系统重启和关机的命令 重启命令&#xff1a;reboot 关机命令&#xff1a;shutdown 这两个命令一般很少用到&#xff0c;我们了解即可。 2 查看日志信息命令 什么是日志&#xff0c;日志就是一个一个普通的文本文件&#xff0c;文件里面记录的是软件运行过程中的信息…

一、初始 Vue

1、Vue 1.1 Vue简介 1.1.1 Vue.js 是什么 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第…

Stable Diffusion 绘画入门教程(webui)-ControlNet

ControlNet可以说在SD里有着举足轻重的地位&#xff0c;如果没有ControlNet&#xff0c;sd的可玩性和实用性将大大降低。 前面几篇文章介绍了 提示词 和 lora&#xff0c;本篇文章介绍sd里最关键的第三个要素–ControlNet 文章目录 一、它具体能干啥1、艺术字2、改变姿态3、其…

Spring解决循环依赖

目录 什么是spring循环依赖 什么情况下循环依赖可以被处理&#xff1f; spring 如何解决循环依赖 创建A这个Bean的流程 答疑 疑问&#xff1a;在给B注入的时候为什么要注入一个代理对象&#xff1f; 初始化的时候是对A对象本身进行初始化&#xff0c;而容器中以及注入到B…

AI新工具(20240221) OOTDiffusion-虚拟试穿技术的实现;Groq-世界上最快的大模型,每秒可以输出近500个token

OOTDiffusion - 虚拟试穿技术的实现 OOTDiffusion是一种基于潜在扩散的虚拟试穿技术&#xff0c;其主要目的是通过实现控制性的换装融合&#xff0c;让用户能够在虚拟环境中试穿不同的服装。该技术通过融合最新的机器学习算法与图像处理技术&#xff0c;为用户提供了一种新颖的…

vue小记——this(1)

原生和Vue中使用this的这几个坑你都知道吗&#xff1f; - 掘金 (juejin.cn) 在JavaScript中&#xff0c;this是一个特殊的关键字&#xff0c;它在函数被调用时自动定义。this的值在函数被调用时决定&#xff0c;取决于调用的上下文&#xff08;context&#xff09;&#xff0c…

盘点 Udemy 上最受欢迎的免费编程课程

之前给大家推荐过一些油管上的免费学习资源&#xff0c;如果您还没有看过的话可以点击这里前往。 今天再给大家推荐一批Udemy上超高质量并且免费的编程课程&#xff0c;有需要的小伙伴可以学起来了。 1. JavaScript Essentials 第一门免费课程是&#xff1a;JavaScript Essen…

h5网页和 Android APP联调,webview嵌入网页,网页中window.open打开新页面,网页只在webview中打开,没有重开一个app窗口

我是h5网页开发&#xff0c;客户app通过webview嵌入我的页面 点击标题window.open跳转到长图页面&#xff0c;客户的需求是在app里新开一个窗口展示长图页面&#xff0c;window.open打开&#xff0c;ios端是符合客户需求的&#xff0c;但是在安卓端他会在当前webview打开 这…

IDEA实现ssh远程连接本地Linux服务器

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xf…

Unity3d Shader篇(七)— 纹理采样

文章目录 前言一、什么是纹理采样&#xff1f;1. 纹理采样的工作原理2. 纹理采样的优缺点优点缺点 二、使用步骤1. Shader 属性定义2. SubShader 设置3. 渲染 Pass4. 定义结构体和顶点着色器函数5. 片元着色器函数 三、效果四、总结使用场景 前言 纹理采样是一种常用的图形学技…

打造智能物品租赁平台:Java与SpringBoot的实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

力扣:869. 重新排序得到 2 的幂

枚举和回溯 1.先把int值转换成char数组&#xff0c;之后再用回溯来枚举出全部的排列顺序&#xff0c;在回溯排列过程中要防止同一个数据的重新使用&#xff0c;同时要计算排列中的数字总和&#xff0c;在进行判断这个数据是否是2的幂&#xff0c;使用递归的方式进行&#xff0…

SparkSQL学习01

目录 1.SparkSQL特点1.1易整合1.2统一的数据访问1.3兼容Hive1.4标准的数据连接 2 SparkSQL编程模型DataFrameDataSet2.1 SQL2.2 DataFrame是什么2.3 DataSet是什么2.4 RDD&#xff0c;DataSet&#xff0c;DataFrame 3 SparkSQL核心编程3.1 编程入口3.2 SparkSQL基本编程3.2.1编…

掌握web控件定位技巧,提升页面操作效率!

简介 在做 Web 自动化时&#xff0c;最根本的就是操作页面上的元素&#xff0c;首先要能找到这些元素&#xff0c;然后才能操作这些元素。工具或代码无法像测试人员一样用肉眼来分辨页面上的元素。那么要如何定位到这些元素&#xff0c;本章会介绍各种定位元素的方法。 web 控…

15. 三数之和 - 力扣(LeetCode)

题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组…

用Python编写计算字母或数字个数的程序

小编简单写了一个实现计算字母数字的程序 实例代码&#xff1a; str_n input("输入一段文本&#xff1a;") int_n 0 zimu_n 0 qita_n 0for i in str_n:if i.isdigit():int_n 1elif i.isalpha():zimu_n 1else:qita_n 1 # 其他字符&#xff08;包括空格、标点…

人脸比对-欧氏距离

欧氏距离人脸比对 欧氏距离判断是否同一人脸 embedding_reshape np.array(face.embedding).reshape((1, -1)) face.embedding_normalize preprocessing.normalize(embedding_reshape) # 欧式距离 diffnp.subtract( self.fixed_face.embedding_normalize,face.embedding_nor…