@Autowired HttpSession & HttpServletRequest原理

原文地址 (我的個(gè)人網(wǎng)站,歡迎訪問):
@Autowired HttpSession & HttpServletRequest原理

問題

在開發(fā)Controller的時(shí)候,經(jīng)常會(huì)需要使用到HttpSessionHttpServletRequest這兩個(gè)接口的實(shí)例對(duì)象。在工作室中的項(xiàng)目,無論是師兄留下的代碼還是自己編寫,基本都是使用比較簡單的,就是直接從方法參數(shù)中獲取,就像下面這樣:

@RequestMapping("/test") //@RestController省略@ResponseBody注解
public ResponseModel doTest(HttpSession session) throws MissingServletRequestParameterException {
        return ResponseModel.ok().body(session.getId());
}

這不失為一個(gè)好辦法,但也有缺點(diǎn):一是如果多個(gè)方法需要會(huì)話操作(如權(quán)限接口)的話會(huì)顯得冗余,而是加長了方法參數(shù)列表降低了代碼可讀性。畢竟Controller中的方法一般都會(huì)代表前臺(tái)某個(gè)參數(shù)。所以我想尋求一種更好的辦法。Spring的依賴注入很方便,那么,Session和Request對(duì)象能不能依賴注入呢?

只有一年多開發(fā)經(jīng)驗(yàn)的我,一開始當(dāng)然以為是不行的。

因?yàn)镾ession和Request的生命周期是會(huì)話級(jí)別和請(qǐng)求級(jí)別,而對(duì)Session、Request進(jìn)行依賴注入的對(duì)象是Controller,是單例對(duì)象。我們都知道,某個(gè)對(duì)象依賴注入一個(gè)生命周期不如自己長的對(duì)象,最后的結(jié)果是被注入的對(duì)象生命周期變得和注入的對(duì)象生命周期一樣長。假如有兩個(gè)不同類的實(shí)例A和B,A這種持有B的引用,B注入進(jìn)A的引用里,那么在A生命周期結(jié)束前Spring不會(huì)再去新注入B對(duì)象,如果A是單例對(duì)象,B定義為prototype,那么實(shí)例B也會(huì)“變成”單例,雞犬升天。這顯然違背我們把B定義為非單例的初衷。另外,在Webx3 開發(fā)文檔 4.4.2 中也說明原生Spring注入無法滿足這種情況,Webx通過<request-contexts>增強(qiáng)以后,Webx Turbine才可以使用@Autowired注入。也讓我一開始對(duì)“Spring無法注入Session”深信不疑。

直到我網(wǎng)上搜到一些所謂獲取session方法大全博客,里面赫然有著autowired注入這種方法。

這 ..

不是說原生Spring不可以嗎webx爸爸 ..
于是默默的寫了一大堆測(cè)試代碼,找了很多文檔資料,最終搞明白了其中的道理,分享如下。先把結(jié)果寫出來,如果有興趣可以再往下看測(cè)試代碼。

Scoped Proxy

簡單介紹一個(gè)Spring對(duì)單例對(duì)象注入非單例實(shí)例的一個(gè)解決方案:Scoped Proxy.

Scoped proxy使用代理模式,不去直接注入這個(gè)類的實(shí)例,而是每次使用的時(shí)候都去創(chuàng)建一個(gè)代理對(duì)象,當(dāng)調(diào)用注入方法時(shí),會(huì)通過其他途徑去調(diào)用原生對(duì)象的方法。所以我們autowired的時(shí)候不是真的注入自己的對(duì)象,只是一個(gè)殼而已。

自己使用bean scoped的例子可以查看開發(fā)文檔:Spring4 開發(fā)文檔: bean scope
簡潔的解釋可以看:spring scoped proxy bean

結(jié)論

知道了Scoped Proxy,想必對(duì)這個(gè)的實(shí)現(xiàn)也了解的差不多了。Spring巧妙地注入了一個(gè)裝飾器代理了真實(shí)對(duì)象的操作,只在需要的時(shí)候獲取真實(shí)的Request和Session對(duì)象,獲取方法Servlet自身實(shí)現(xiàn)好了,和Servlet類獲取Request和Session應(yīng)該大同小異。所以獲取Session和Request的幾種方法中:

  • @Autowired獲取到的是裝飾器對(duì)象
  • 方法參數(shù)獲取到的是真實(shí)對(duì)象
  • 無論是@Autowired還是方法參數(shù)的request,request.getSession()獲取到的是真實(shí)Session對(duì)象

shiro session

實(shí)際測(cè)試中,發(fā)現(xiàn)整合Shiro以后HttpSession的差異。

雖然都是注入了一個(gè)裝飾器對(duì)象,但是Spring中,無論是裝飾器調(diào)用的,還是方法參數(shù)獲取到的,或者是request.getSession()獲取到的HttpSession都是真實(shí)的HttpSession對(duì)象,hashCode都是不變的,非分布式的單服務(wù)器實(shí)例下,一個(gè)會(huì)話只有一個(gè)HttpSession

整合Shiro以后,無論是裝飾器調(diào)用的,還是方法參數(shù)獲取到的,或者是request.getSession()獲取到的HttpSession都是代理對(duì)象,每次獲取到的session,只是"看上去"和真實(shí)Session一樣,實(shí)際上每次執(zhí)行controller獲取到的session都是不同的實(shí)例,hashcode是不一樣的。

這是因?yàn)檎蟂hiro以后,StoppingAwareProxiedSession繼承了原來的HttpSession實(shí)現(xiàn)類(Tomcat中是StandardSession),屬于shiro自定義的session的子類。通過這個(gè)代理對(duì)象的源碼,所有與session相關(guān)的方法都是通過它內(nèi)部委托類delegate進(jìn)行的,而到delegate的類型其實(shí)也是 org.apache.catalina.session.StandardSessionFacade ,也就是說,兩者在操作session時(shí),都是用同一個(gè)類型的session。

Shiro用了一層代理來包裝HttpSessionHttpServletRequest這兩個(gè)接口實(shí)例,如果用@Autowired注入,算上@Autowired的裝飾器,是兩層代理。

Shiro包裝HttpSession

測(cè)試

測(cè)試demo和測(cè)試數(shù)據(jù)見:

我的github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容