原文地址 (我的個(gè)人網(wǎng)站,歡迎訪問):
@Autowired HttpSession & HttpServletRequest原理
問題
在開發(fā)Controller的時(shí)候,經(jīng)常會(huì)需要使用到HttpSession和HttpServletRequest這兩個(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)然以為是不行的。

<request-contexts>增強(qiáng)以后,Webx Turbine才可以使用@Autowired注入。也讓我一開始對(duì)“Spring無法注入Session”深信不疑。
直到我網(wǎng)上搜到一些所謂獲取session方法大全博客,里面赫然有著autowired注入這種方法。
這 ..

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用了一層代理來包裝HttpSession和HttpServletRequest這兩個(gè)接口實(shí)例,如果用@Autowired注入,算上@Autowired的裝飾器,是兩層代理。
測(cè)試
測(cè)試demo和測(cè)試數(shù)據(jù)見: