Spring boot 注入成員變量HttpServletRequest的原理

前言

最近做項目,springboot項目,本來我們在controller的requestmapping取參數值或者返回寫時,使用方法參數,但是發現老項目直接注入了成員變量,Spring本身是單例的,如果是成員變量注入,那么也是單例的,怎么實現不同的請求讀取不同的參數呢,如果實現線程安全呢,筆者立馬想到了ThreadLocal,但是如果要說就是這個原理,那么必須源碼證明。

準備demo

簡單寫一個demo

@RestController
public class DemoController {@Autowiredprivate HttpServletRequest request;@GetMapping("/hello")public String demo(String param) {request.getParameterMap().forEach((k,v)-> System.out.println(k + " : " + Arrays.toString(v)));return param + ":hello";}
}

只寫了?HttpServletRequest,實際上HttpServletResponse亦是如此。

分析demo,注入的HttpServletRequest是接口類型,那么在Boot啟動中就會動態代理實現,由于是接口,可以推測是JDK動態代理,debug果然如此。

源碼分析

根據debug,JDK動態代理注入了接口的實現類,關鍵在于InvocationHandler,Spring使用如下:

org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler

里面有關鍵代碼

return method.invoke(this.objectFactory.getObject(), args);

this.objectFactory.getObject()這句決定線程安全

看看Spring Bean下JDK是怎么動態代理注入的

可以看到JDK動態代理在Spring注入的時候,把這個factory注入了InvocationHandler

其中的handler的invoke方法,這里實際上還要其他類的讀取埋點。

這里的invoke僅僅是反射,關鍵還是?HttpServletRequest的對象來源,跟蹤讀取邏輯

org.springframework.web.context.request.RequestContextHolder

到此就明確了,注入的成員變量,動態代理后使用Threadlocal處理,所以線程安全,因為每次請求都是線程請求,那么原始的HttpServletRequest對象怎么塞進去的呢,就要看filter的了

org.springframework.web.filter.RequestContextFilter

在doFilter時,執行

同理在org.springframework.web.servlet.FrameworkServlet也會再次讀取和寫入

這里是為了,如果filter被去除的時候可以有值,再次保底,并且在結束時rest

?只不過這個rest有點奇怪

對象并沒有清除,還在,說明即使?FrameworkServlet后還可以獲取,因為有filter可能會在這個后面執行,如果干掉,很可能就不能讀取了。會在RequestContextFilter后面remove;如果我們去掉這個filter,那么需要自定義一個filter實現remove防止內存泄漏。

清除當前線程及子線程ThreadLocal

public static void resetRequestAttributes() {requestAttributesHolder.remove();inheritableRequestAttributesHolder.remove();
}

至此?HttpServletRequest的成員變量注入邏輯,即ThreadLocal變量,所以請求可以正常訪問

總結

實際上這種用法很多項目都用了,只不過我們寫代碼下意思的通過方法參數來規避線程安全性,這種想法是有益的,可以從源頭規避風險,不過實際上Spring也幫我們考慮了這個問題,相當于使用RequestContextFilter做了保底措施。源碼分析實際上是知所以然,尤其是我們自己寫公共SDK時,可以把這種設計放在代碼中,實現優雅和保底邏輯。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/14955.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/14955.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/14955.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C語言】指針(三)

目錄 一、字符指針 1.1 ? 使用場景 1.2 ? 有關字符串筆試題 二、數組指針 2.1 ? 數組指針變量 2.2 ? 數組指針類型 2.3 ? 數組指針的初始化 三、數組指針的使用 3.1 ? 二維數組和數組名的理解 3.2 ? 二維數組傳參 四、函數指針 4.1 ? 函數的地址 4.2 ? 函數…

JAVA面試題大全(十一)

1、為什么要使用 hibernate? 對JDBC訪問數據庫的代碼做了封裝,大大簡化了數據訪問層繁瑣的重復性代碼基于jdbc的主流持久化框架,是一個優秀的ORM實現,很大程度的簡化了dao層的編碼工作使用java的反射機制性能好,是一個…

【STL】C++ list 基本使用

目錄 一 list 常見構造 1 空容器構造函數(默認構造函數) 2 Fill 構造函數 3 Range 構造函數 4 拷貝構造函數 二 list迭代器 1 begin && end 2 rbegin && rend 三 list 容量操作 四 list 修改操作 1 assign 2 push_front &a…

【深度學習中的數據預處理技巧:提升模型性能的關鍵步驟】

文章目錄 前言數據標準化(Normalization)數據增強(Data Augmentation)缺失值處理(Handling Missing Values)特征編碼(Feature Encoding)結論 前言 在深度學習領域,數據預…

牛客NC362 字典序排列【中等 DFS Java/Go/PHP】

題目 題目鏈接: https://www.nowcoder.com/practice/de49cf70277048518314fbdcaba9b42c 解題方法 DFS,剪枝Java代碼 import java.util.*;public class Solution {/*** 代碼中的類名、方法名、參數名已經指定,請勿修改,直接返回…

【小筆記】如何在docker中更新或導入neo4j數據?

如何在docker中更新或導入neo4j數據? (1)背景: 我嘗試了4.4.9和5.19.0版本的Neo4j社區版,基于他們的鏡像創建容器后,需要導入我準備好的csv文件或dump文件,因為數據量非常大,所以采…

2024電工杯數學建模B題Python代碼+結果表數據教學

2024電工杯B題保姆級分析完整思路代碼數據教學 B題題目:大學生平衡膳食食譜的優化設計及評價 以下僅展示部分,完整版看文末的文章 import pandas as pd df1 pd.read_excel(附件1:1名男大學生的一日食譜.xlsx) df1# 獲取所有工作表名稱 e…

HarmonyOS-MPChart繪制一條虛實相接的曲線

本文是基于鴻蒙三方庫mpchart(OpenHarmony-SIG/ohos-MPChart)的使用,自定義繪制方法,繪制一條虛實相接的曲線。 mpchart本身的繪制功能是不支持虛實相接的曲線的,要么完全是實線,要么完全是虛線。那么當我…

面試總結之:socket線路切換

"socket線路切換"通常指的是在網絡通信過程中,根據當前網絡狀態或策略來動態更換數據傳輸路徑的技術。這種技術可以提高通信的可靠性和性能。 在實際應用中,線路切換可能涉及到多種技術,例如: 負載均衡:根據每條路徑的當前負載情況,動態地選擇一條較為空閑的路…

MySql超大Sql文件導入效率優化 —— 筑夢之路

使用場景 日常我們對mysql數據庫、mariadb數據庫進行定時備份,而隨著時間增長,導出來的備份文件越來越大,使用備份sql文件進行還原的時候,大文件非常慢,有些要執行很長時間,效率很低。 如何優化&#xff…

根據多個坐標經緯度獲取到中心點的經緯度,scala語言

文章目錄 前言scala 代碼 總結 前言 Scala 語言 通過多個經緯度坐標點, 計算出中心點, 這里使用的是 Scala 語言,其他的語言需要自行轉換。求出來的并不是原有的點,而是原有點的中心位置的點。 scala 代碼 package com.dw.process.midimport java.lang.Double.pa…

C語言 | Leetcode C語言題解之第97題交錯字符串

題目&#xff1a; 題解&#xff1a; bool isInterleave(char* s1, char* s2, char* s3) {int n strlen(s1), m strlen(s2), t strlen(s3);int f[m 1];memset(f, 0, sizeof(f));if (n m ! t) {return false;}f[0] true;for (int i 0; i < n; i) {for (int j 0; j &l…

基于UDP的tftp的文件傳輸

#define SER_PORT 69 #define SER_IP "192.168.125.71" #define CLT_PORT 6666 #define CLT_IP "192.168.125.158" int main(int argc, const char *argv[]) {//創建套接字文件描述符int cfd socket(AF_INET,SOCK_DGRAM,0);if(cfd -1){perror("sock…

vue2-computed,vue3+watch 前端實現列表搜索,結合filter+some+indexOf

vue2 computed實現 computed: {FBAAddressListComputed () {if (!this.fbaInput) return this.FBAAddressListconst lowerCaseInput this.fbaInput.toLowerCase()return this.FBAAddressList.filter((item) > {return [item.fbaCode, item.zipCode, item.countryCode, ite…

六(3)、RTKLIB源碼解析 — [postpos]: execses(antpos, outhead, procpos)

目錄 一、antpos() 1.1 avepos() 1.2 getstapos() 二、outhead() 三、procpos() 3.1 inputobs() 3.1.1 nexto

牛客周賽 Round 42

小紅叕戰小紫 #include<bits/stdc.h> using namespace std; void solve(){string s;cin>>s;if(s.length()<1)cout<<"yukari";else cout<<"kou"<<endl; } int main(){ios::sync_with_stdio(false), cin.tie(0), cout.tie…

Qt時間類、日期類、時間日期類介紹

一.時間類&#xff08;QTime&#xff09; Qt中的時間類QTime是用來處理時間的類&#xff0c;它可以表示一個特定的時間&#xff0c;精確到毫秒。QTime類提供了一些方法來訪問和操作時間&#xff0c;例如獲取小時、分鐘、秒以及毫秒部分&#xff0c;還可以進行時間的比較和運算。…

Python列表,元組,集合,字典詳解一篇搞懂

目錄 介紹 列表(List) 集合(Set) 字典(Dict) 元組(Tuple) 列表 列表定義 ?編輯 列表切片 列表常用方法 append extend ?編輯 insert ?編輯 remove pop ?編輯 clear ?編輯 列表修改元素 sort 升序 倒序 reverse count ?編輯 index 淺拷貝和深拷貝 …

《書生·浦語大模型實戰營》第一課 學習筆記:書生·浦語大模型全鏈路開源體系

文章大綱 1. 簡介與背景智能聊天機器人與大語言模型目前的開源智能聊天機器人與云上運行模式 2. InternLM2 大模型 簡介3. 視頻筆記&#xff1a;書生浦語大模型全鏈路開源體系內容要點從模型到應用典型流程全鏈路開源體系 4. 論文筆記:InternLM2 Technical Report簡介軟硬件基礎…

基于Java的地震震中附近城市分析實戰

目錄 前言 一、空間數據說明 1、空間查詢 二、Java后臺開發 1、模型層設計與實現 2、控制層設計與實現 三、Leaflet地圖開發 1、地震震中位置展示 2、附近城市展示 3、成果展示 總結 前言 隨著全球氣候變化和地殼活動的不斷演變&#xff0c;地震作為一種自然災害&…