Springboot注入成員變量HttpServletRequest的原理分析
前言
最近做項(xiàng)目,springboot項(xiàng)目,本來(lái)我們?cè)赾ontroller的requestmapping取參數(shù)值或者返回寫時(shí),使用方法參數(shù),但是發(fā)現(xiàn)老項(xiàng)目直接注入了成員變量,Spring本身是單例的,如果是成員變量注入,那么也是單例的,怎么實(shí)現(xiàn)不同的請(qǐng)求讀取不同的參數(shù)呢,如果實(shí)現(xiàn)線程安全呢,筆者立馬想到了ThreadLocal,但是如果要說(shuō)就是這個(gè)原理,那么必須源碼證明。
準(zhǔn)備demo
簡(jiǎn)單寫一個(gè)demo
@RestController public class DemoController { @Autowired private 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,實(shí)際上HttpServletResponse亦是如此。
分析demo,注入的HttpServletRequest是接口類型,那么在Boot啟動(dòng)中就會(huì)動(dòng)態(tài)代理實(shí)現(xiàn),由于是接口,可以推測(cè)是JDK動(dòng)態(tài)代理,debug果然如此。
源碼分析
根據(jù)debug,JDK動(dòng)態(tài)代理注入了接口的實(shí)現(xiàn)類,關(guān)鍵在于InvocationHandler
Spring使用如下:
org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler
里面有關(guān)鍵代碼
return method.invoke(this.objectFactory.getObject(), args);
this.objectFactory.getObject()這句決定線程安全
看看Spring Bean下JDK是怎么動(dòng)態(tài)代理注入的
可以看到JDK動(dòng)態(tài)代理在Spring注入的時(shí)候,把這個(gè)factory注入了InvocationHandler
其中的handler的invoke方法,這里實(shí)際上還要其他類的讀取埋點(diǎn)。
這里的invoke僅僅是反射,關(guān)鍵還是 HttpServletRequest的對(duì)象來(lái)源,跟蹤讀取邏輯
org.springframework.web.context.request.RequestContextHolder
到此就明確了,注入的成員變量,動(dòng)態(tài)代理后使用Threadlocal處理,所以線程安全,因?yàn)槊看握?qǐng)求都是線程請(qǐng)求,那么原始的HttpServletRequest對(duì)象怎么塞進(jìn)去的呢,就要看filter的了
org.springframework.web.filter.RequestContextFilter
在doFilter時(shí),執(zhí)行
同理在org.springframework.web.servlet.FrameworkServlet也會(huì)再次讀取和寫入
這里是為了,如果filter被去除的時(shí)候可以有值,再次保底,并且在結(jié)束時(shí)rest
只不過(guò)這個(gè)rest有點(diǎn)奇怪
對(duì)象并沒(méi)有清除,還在,說(shuō)明即使 FrameworkServlet后還可以獲取,因?yàn)橛衒ilter可能會(huì)在這個(gè)后面執(zhí)行,如果干掉,很可能就不能讀取了。
會(huì)在RequestContextFilter后面remove;如果我們?nèi)サ暨@個(gè)filter,那么需要自定義一個(gè)filter實(shí)現(xiàn)remove防止內(nèi)存泄漏。
清除當(dāng)前線程及子線程ThreadLocal
public static void resetRequestAttributes() { requestAttributesHolder.remove(); inheritableRequestAttributesHolder.remove(); }
至此 HttpServletRequest的成員變量注入邏輯,即ThreadLocal變量,所以請(qǐng)求可以正常訪問(wèn)
總結(jié)
實(shí)際上這種用法很多項(xiàng)目都用了,只不過(guò)我們寫代碼下意思的通過(guò)方法參數(shù)來(lái)規(guī)避線程安全性,這種想法是有益的,可以從源頭規(guī)避風(fēng)險(xiǎn),不過(guò)實(shí)際上Spring也幫我們考慮了這個(gè)問(wèn)題,相當(dāng)于使用RequestContextFilter做了保底措施。
源碼分析實(shí)際上是知所以然,尤其是我們自己寫公共SDK時(shí),可以把這種設(shè)計(jì)放在代碼中,實(shí)現(xiàn)優(yōu)雅和保底邏輯。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot集成WebServlet出現(xiàn)自定義servlet請(qǐng)求失敗的問(wèn)題解決方案
- SpringBoot里使用Servlet進(jìn)行請(qǐng)求的實(shí)現(xiàn)示例
- springboot掃描自定義的servlet和filter代碼詳解
- SpringBoot3.1.2 引入Swagger報(bào)錯(cuò)Type javax.servlet.http.HttpServletRequest not present解決辦法
- 解決IDEA啟動(dòng)springboot項(xiàng)目報(bào)錯(cuò)java.lang.ClassNotFoundException:?javax.servlet.ServletContext
- SpringBoot獲取HttpServletRequest的3種方式總結(jié)
- Springboot如何添加server.servlet.context-path相關(guān)使用
- SpringBoot項(xiàng)目找不到j(luò)avax.servlet.Filter的問(wèn)題及解決
- SpringBoot如何切換成其它的嵌入式Servlet容器(Jetty和Undertow)
相關(guān)文章
Spring Boot2深入分析解決java.lang.ArrayStoreException異常
這篇文章介紹了Spring Boot2深入分析解決java.lang.ArrayStoreException異常的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12java Socket實(shí)現(xiàn)簡(jiǎn)單模擬HTTP服務(wù)器
這篇文章主要介紹了java Socket實(shí)現(xiàn)簡(jiǎn)單模擬HTTP服務(wù)器,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05利用Spring Social輕松搞定微信授權(quán)登錄的方法示例
這篇文章主要介紹了利用Spring Social輕松搞定微信授權(quán)登錄的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12MyBatis-Plus 之selectMaps、selectObjs、selectCount、selectO
本文主要介紹了MyBatis-Plus 之selectMaps、selectObjs、selectCount、selectOne的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Springmvc如何實(shí)現(xiàn)向前臺(tái)傳遞數(shù)據(jù)
這篇文章主要介紹了Springmvc如何實(shí)現(xiàn)向前臺(tái)傳遞數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07datatables 帶查詢條件java服務(wù)端分頁(yè)處理實(shí)例
本篇文章主要介紹了datatables 帶查詢條件java服務(wù)端分頁(yè)處理實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06Springboot Cache @CacheEvict 無(wú)法模糊刪除的解決方案
這篇文章主要介紹了Springboot Cache @CacheEvict 無(wú)法模糊刪除的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12