【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.pswp.cn/web/77017.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77017.shtml
英文地址,請注明出處:http://en.pswp.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…