【SpringBoot】HttpServletRequest获取使用及失效问题(包含@Async异步执行方案)

目录

1. 在 Controller 方法中作为参数注入

2.使用 RequestContextHolder

(1)失效问题 

(2)解决方案一:

 (3)解决方案二:

3、使用@AutoWrite自动注入HttpServletRequest

跨线程调用失效问题:

补充:什么是@Async:

(1) 启用异步支持

(2)在你想异步执行的方法上加 @Async

(3)调用这个方法(注意!不要在同一个类中自调用)

(4)注意事项

(5)完整示例:


        大家好,我是jstart千语。我们做项目时,通常要使用到HttpServletRequest来进行对请求响应的消息进行处理,本篇给大家带来三种获取HttpServletRequest的方式。

1. 在 Controller 方法中作为参数注入

SpringMVC会自动注入:


@RestController
public class MyController {@GetMapping("/example")public String example(HttpServletRequest request) {String clientIp = request.getRemoteAddr();return "Client IP: " + clientIp;}
}

2.使用 RequestContextHolder

如果你不在 Controller 中,而是在 Service、Util 类等位置想获取当前的请求对象,可以使用:

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;public class MyService {public void doSomething() {// 获取当前请求的上下文ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes != null) {// 获取 HttpServletRequestHttpServletRequest request = attributes.getRequest();// 使用请求信息(如获取 Header、参数等)String userAgent = request.getHeader("User-Agent");String paramValue = request.getParameter("paramName");// 获取 HttpServletResponseHttpServletResponse response = attributes.getResponse();}}
}

(1)失效问题 

注意点:

        RequestContextHolder 使用的是 ThreadLocal 存储当前请求的上下文信息。一旦你离开当前请求线程(例如新开线程),这些上下文信息就不会自动传递过去。如:

@RequestMapping("/async-test")
public String asyncTest() {new Thread(() -> {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 值为 null}).start();return "OK";
}

(2)解决方案一:

提前取出你想要的值,然后以参数形式传入线程内部,这样就不会有上下文丢失的问题。

@RequestMapping("/async-test")
public String asyncTest(HttpServletRequest request) {// 主线程中先获取你需要的信息String uri = request.getRequestURI();String clientIp = request.getRemoteAddr();// 把值作为参数传给异步线程new Thread(() -> {System.out.println("异步线程中访问 URI: " + uri);System.out.println("异步线程中客户端 IP: " + clientIp);}).start();return "OK";
}

 (3)解决方案二:

如果用的是 @Async,可以启用上下文传递。

Spring 5.3 开始提供了 TaskDecorator,可以用它将当前的请求上下文“包装”起来传给异步线程。

 1、定义一个TaskDecorator:

import org.springframework.core.task.TaskDecorator;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;public class ContextCopyingDecorator implements TaskDecorator {@Overridepublic Runnable decorate(Runnable runnable) {RequestAttributes context = RequestContextHolder.getRequestAttributes();return () -> {try {RequestContextHolder.setRequestAttributes(context);runnable.run();} finally {RequestContextHolder.resetRequestAttributes();}};}
}

2、配置线程池使用这个装饰器:

@Configuration
@EnableAsync
public class AsyncConfig {@Beanpublic TaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setTaskDecorator(new ContextCopyingDecorator());executor.setCorePoolSize(5);executor.initialize();return executor;}
}



3、使用@AutoWrite自动注入HttpServletRequest

说明:

        Spring 注入的是一个代理对象(HttpServletRequest 是 request scope 的 bean),这个代理在每个请求到达时会根据当前线程,自动定位到当前线程的真实请求对象。

        通过自动注入的HttpServletRequest本质上也是一个RequestContextHolder,代理内部每次调用方法(比如 getRequestURI())时,都会通过 RequestContextHolder.getRequestAttributes() 找 当前线程绑定的 request 对象。

        所以自动注入的方式不适用的场景跟使用RequestContextHolder相同

使用示例:

@Component
public class LogService {@Autowiredprivate HttpServletRequest request;public void printLog() {System.out.println("请求地址: " + request.getRequestURI());}
}

跨线程调用失效问题:

  1. 使用自动注入的方式,因为注入的是一个代理对象。
  2. 代理对象是和线程绑定的,调用HttpServletRequest调用方法()如getRequestURI()),会通过RequestContextHolder.getRequestAttributes(),找 当前线程绑定的 request 对象
  3. 所以如果将主线程的HttpServletRequest赋值给了其他线程使用,也是使用不到的

失效问题举例详解:

1、把request对象放入全局变量:

public void storeRequestObject() {globalMap.put("lastRequest", request); }

2、另一个线程取出来使用:

// 假设这是另一个线程:
HttpServletRequest req = globalMap.get("lastRequest");
String uri = req.getRequestURI(); // ❌ 此时 request 对应的 ThreadLocal 是空的,报错!

你把 request 这个代理对象存进去后,其他线程如果取出来用,就会出错。因为 这个线程没有设置自己的 RequestContextHolder,调用时会拿不到实际的 request 实例,就会报错

解决:完成线程之间共享

存储真正的 request 信息,而不是 request 对象

public void storeRequestInfo() {String uri = request.getRequestURI(); // 当前线程获取globalMap.put("lastRequestUri", uri); // 只存具体信息,不存对象
}



补充:什么是@Async:

        @Async 是 Spring 提供的一个注解,用来让你的方法异步执行(非阻塞)。它背后是线程池 + AOP 实现的。你只需要加个注解,Spring 就会帮你把方法在新线程里执行,非常适合处理不需要立刻返回的任务,比如发送邮件、日志记录、异步通知等等。

(1) 启用异步支持

在你的 Spring Boot 启动类或者配置类上加上:

@EnableAsync
@SpringBootApplication
public class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}
}

(2)在你想异步执行的方法上加 @Async

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class MyService {@Asyncpublic void doAsyncTask() {System.out.println("开始执行异步任务,线程名:" + Thread.currentThread().getName());try {Thread.sleep(3000); // 模拟耗时任务} catch (InterruptedException e) {e.printStackTrace();}System.out.println("异步任务完成");}
}

(3)调用这个方法(注意!不要在同一个类中自调用)

@RestController
public class TestController {private final MyService myService;public TestController(MyService myService) {this.myService = myService;}@GetMapping("/start-task")public String startTask() {myService.doAsyncTask(); // 异步执行,不会阻塞这个接口的返回return "任务已提交";}
}

(4)注意事项

  • @Async 方法必须是 public 的。
  • @Async 方法不能是自己类内部调用(会失效),必须是通过 Spring 容器的代理调用(也就是从别的类调它)。
  • 返回值可以是 void、Future<T>、CompletableFuture<T> 等。



(5)完整示例:

示例结构:

  • @Async 异步方法
  • 使用 RequestContextHolder 获取请求信息
  • 配置线程池 + 自定义 TaskDecorator
  • 测试 Controller 发起异步请求

a.引入依赖(spring-boot-starter-web 和 spring-boot-starter 已包含 @Async 所需依赖)

<!-- pom.xml -->
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>

b.自定义 TaskDecorator:让请求上下文穿透到异步线程

import org.springframework.core.task.TaskDecorator;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;public class ContextCopyingTaskDecorator implements TaskDecorator {@Overridepublic Runnable decorate(Runnable runnable) {RequestAttributes context = RequestContextHolder.getRequestAttributes();return () -> {try {RequestContextHolder.setRequestAttributes(context);runnable.run();} finally {RequestContextHolder.resetRequestAttributes();}};}
}

c.配置异步线程池并应用装饰器

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;@Configuration
@EnableAsync
public class AsyncConfig {@Bean("customTaskExecutor")public TaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(25);executor.setThreadNamePrefix("async-exec-");executor.setTaskDecorator(new ContextCopyingTaskDecorator());executor.initialize();return executor;}
}

d. 异步服务类中使用 @Async 并获取请求信息

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;@Service
public class AsyncService {@Async("customTaskExecutor")public void processAsyncTask() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String uri = request.getRequestURI();String clientIp = request.getRemoteAddr();System.out.println("【异步线程】处理请求 URI: " + uri);System.out.println("【异步线程】客户端 IP: " + clientIp);// 模拟耗时操作try {Thread.sleep(3000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("【异步线程】任务处理完毕");}
}

e.Controller 提交异步任务

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController {private final AsyncService asyncService;public TestController(AsyncService asyncService) {this.asyncService = asyncService;}@GetMapping("/start-async")public String startAsyncTask() {asyncService.processAsyncTask(); // 调用异步方法return "异步任务已提交,主线程立即返回";}
}

f.测试结果示例

http://localhost:8080/start-async

控制台输出类似:

【异步线程】处理请求 URI: /start-async
【异步线程】客户端 IP: 127.0.0.1
【异步线程】任务处理完毕

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

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

相关文章

mfc学习(一)

mfc为微软创建的一个类qt框架的客户端程序&#xff0c;只不过因为微软目前有自己 的亲身儿子C#&#xff08;.net&#xff09;,所以到2010没有进行维护。然后一些的工业企业还在继续进行维护相关的内容。我目前就接手一个现在这样的项目&#xff0c;其实本质与qt的思路是差不多的…

HarmonyOS:一多能力介绍:一次开发,多端部署

概述 如果一个应用需要在多个设备上提供同样的内容&#xff0c;则需要适配不同的屏幕尺寸和硬件&#xff0c;开发成本较高。HarmonyOS 系统面向多终端提供了“一次开发&#xff0c;多端部署”&#xff08;后文中简称为“一多”&#xff09;的能力&#xff0c;可以基于一种设计…

秒出PPT推出更强版本,AI PPT工具进入新纪元!

在现代职场中&#xff0c;PPT是我们沟通和展示信息的重要工具。无论是做产品演示&#xff0c;还是准备工作汇报&#xff0c;一份精美的PPT能大大提升演示效果。然而&#xff0c;传统的PPT制作往往需要消耗大量时间&#xff0c;尤其是在排版、设计和内容调整上。如今&#xff0c…

Godot开发2D冒险游戏——第二节:主角光环整起来!

变量的作用域 全局变量&#xff0c;局部变量&#xff0c;导出变量&#xff08;可以在检查器当中快速查看&#xff09; 为玩家添加移动动画 现在游戏的玩家还只是在滑行&#xff0c;我们需要再添加玩家每个方向上的移动效果 删除原先的Item节点&#xff0c;创建一个动画精灵…

颠覆传统NAS体验:耘想WinNAS让远程存储如同本地般便捷

在当今数据爆炸的时代&#xff0c;网络附加存储(NAS)已成为许多企业和个人用户的必备设备。然而&#xff0c;传统硬件NAS解决方案存在诸多限制&#xff0c;如高额成本、复杂设置和有限的远程访问能力。耘想WinNAS以其创新的软件解决方案&#xff0c;彻底改变了这一局面&#xf…

新市场环境下新能源汽车电流传感技术发展前瞻

新能源革命重构产业格局 在全球碳中和战略驱动下&#xff0c;新能源汽车产业正经历结构性变革。国际清洁交通委员会&#xff08;ICCT&#xff09;最新报告显示&#xff0c;2023年全球新能源汽车渗透率突破18%&#xff0c;中国市场以42%的市占率持续领跑。这种产业变革正沿着&q…

STM32之DHT11温湿度传感器---附代码

DHT11简介 DHT11的供电电压为 3&#xff0d;5.5V。 传感器上电后&#xff0c;要等待 1s 以越过不稳定状态在此期间无需发送任何指令。 电源引脚&#xff08;VDD&#xff0c;GND&#xff09;之间可增加一个100nF 的电容&#xff0c;用以去耦滤波。 DATA 用于微处理器与DHT11之间…

#define STEUER_A_H {PWM_A_ON}

目录 一、括号的区别 二、实例讲解 三、注意事项 四、总结 五、补充 一、括号的区别 大括号 {}: 在 C/C 中&#xff0c;大括号一般用于表示一个代码块或结构体、集合等。例如&#xff1a; 用于定义函数体、控制结构&#xff08;如 if、for&#xff09;的代码块。用于初始化…

Redis 缓存—处理高并发问题

Redis的布隆过滤器、单线程架构、双写一致性、比较穿透、击穿及雪崩、缓存更新方案及分布式锁。 1 布隆过滤器 是一种高效的概率型数据结构&#xff0c;用于判断元素是否存在。主要用于防止缓存穿透&#xff0c;通过拦截不存在的数据查询&#xff0c;避免击穿数据库。 原理&…

【玩转全栈】—— 无敌前端究极动态组件库--Inspira UI

目录 Inspira UI 介绍 配置环境 使用示例 效果&#xff1a; Inspira UI 学习视频&#xff1a; 华丽优雅 | Inspira UI快速上手_哔哩哔哩_bilibili 官网&#xff1a;https://inspira-ui.com/ Inspira UI 介绍 Inspira UI 是一个设计精美、功能丰富的用户界面库&#xff0c;专为…

【OpenCV图像处理实战】从基础操作到工业级应用

目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现&#xff08;6个案例&#xff09;案例1&#xff1a;图像基本操作案例2&#xff1a;边缘检测案例3&…

fastjson使用parseObject转换成JSONObject出现将字符特殊字符解析解决

现象&#xff1a;将字符串的${TARGET_VALUE}转换成NULL字符串了问题代码&#xff1a; import com.alibaba.fastjson.JSON;JSONObject config JSON.parseObject(o.toString()); 解决方法&#xff1a; 1.更换fastjson版本 import com.alibaba.fastjson2.JSON;或者使用其他JS…

Docker Compose 和 Kubernetes(k8s)区别

前言&#xff1a;Docker Compose 和 Kubernetes&#xff08;k8s&#xff09;是容器化技术中两个常用的工具&#xff0c;但它们的定位、功能和适用场景有显著区别。以下是两者的核心对比&#xff1a; ​​1. 定位与目标​​ ​​特性​​ ​​Docker Compose​​ ​​Kubernet…

【21天学习打卡挑战赛】如何学习WEB安全:逼自己在短时间掌握WEB安全核心内容

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

Oracle数据库巡检脚本

1.查询实例信息 SELECT INST_ID, INSTANCE_NAME, TO_CHAR(STARTUP_TIME, YYYY-MM-DD HH24:MI:SS) AS STARTUP_TIME FROM GV$INSTANCE ORDER BY INST_ID; 2.查看是否归档 archive log list 3.查看数据库参数 SELECT NAME , TYPE , VALUE FROM V$PARAMETER ORDER BY NAME; 4.…

Windows 安装 JDK

下载 Java8 的下载直接访问&#xff1a;https://www.oracle.com/java/technologies/downloads/#java8-windows https://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html 接受协议后点击下载&#xff0c;再输入账号信息就可以下载了。 如果…

强化学习核心原理及数学框架

1. 定义与核心思想 强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种通过智能体&#xff08;Agent&#xff09;与环境&#xff08;Environment&#xff09;的持续交互来学习最优决策策略的机器学习范式。其核心特征为&#xff1a; ​​试错学习​​&#x…

Netty前置基础知识之BIO、NIO以及AIO理论详细解析和实战案例

前言 Netty是什么&#xff1f; Netty 是一个基于 Java 的 ​高性能异步事件驱动网络应用框架&#xff0c;主要用于快速开发可维护的协议服务器和客户端。它简化了网络编程的复杂性&#xff0c;特别适合构建需要处理海量并发连接、低延迟和高吞吐量的分布式系统。 1)Netty 是…

TIM输入捕获知识部分

越往左&#xff0c;频率越高&#xff1b;越往右&#xff0c;频率越低。【越紧凑&#xff0c;相同时间&#xff0c;次数越多】 计算频率的方法&#xff1a;测评法、测周法、中界频率。 频率的定义&#xff1a;1s内出现了多少个重复的周期 测评法就是从频率的定义出发的&#…

4.4 记忆机制与上下文管理:短期与长期记忆的设计与应用

记忆机制与上下文管理已成为智能代理&#xff08;Agent&#xff09;系统实现高效、智能化行为的核心技术。记忆机制通过短期记忆&#xff08;Short-Term Memory, STM&#xff09;和长期记忆&#xff08;Long-Term Memory, LTM&#xff09;支持Agent存储、检索和利用信息&#x…