天天看點

SpringMVC之自動注入Request對象

前幾天看了上司寫的一段代碼,在Controller中注入了HttpServletRequest,形式如下所示:

當時看到了這一段代碼,首先想到的是AutowiredRequestController是一個singleton的bean,HttpServletRequest是一個變化的共享變量,每個請求對象都是不一樣的,這樣寫不會有線程安全問題嗎?帶着疑問去翻了翻SpringMVC的源碼,結論是:不會有線程安全問題!!!不會有線程安全問題!!!!不會有線程安全問題!!!下面我們來分析一下:

SpringMVC之自動注入Request對象

之前也說過會調用AbstractApplicationContext中的refresh方法進行Bean的組裝初始化的過程,在refresh()這個方法中會調用:postProcessBeanFactory()這個方法,設定一些需要預先處理的Bean。而XmlWebApplicationContext繼承了AbstractRefreshableWebApplicationContext,AbstractRefreshableWebApplicationContext中重寫了postProcessBeanFactory這個方法,我們去AbstractRefreshableWebApplicationContext這個類中看一下這個方法的内容:

WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);這個方法是我們需要重點分析的類,我進去看一下:

在這個類中注冊了Web開發相關的三個Scope:RequestScope、SessionScope和全局的SessionScope以及一個ServletContextScope。接下來了注冊了幾個實作了ObjectFactory的Bean。RequestObjectFactory、RequestObjectFactory、SessionObjectFactory、WebRequestObjectFactory。這幾個類是在Controller中注入Request和Response的關鍵。我們先記住RequestObjectFactory這個類。這幾個類被放到了resolvableDependencies中。resolvableDependencies這個Map中存放的是在Autowire時所要使用到的bean的類型和對應的Object。

下面我們去看一下在Controller中注入Request的一個過程。首先看一下相應的一些調用鍊:

SpringMVC之自動注入Request對象

這個調用鍊比較長,調用鍊的執行過程也很複雜,這裡先不過多展開的,我們直接進入到關鍵代碼中。先進入到DefaultListableBeanFactory的findAutowireCandidates這個方法中,看一些關鍵代碼:

在上面的代碼中我們看到了resolvableDependencies這個屬性。當注入HttpServletRequest的時候,requiredType值是HttpServletRequest.class,我們還記得在resolvableDependencies中放入了ServletRequest.class這個key。是以 if (autowiringType.isAssignableFrom(requiredType)) 這個判斷會傳回true,接着會取得RequestObjectFactory這個對象。接着會調用AutowireUtils.resolveAutowiringValue這個方法進一步的解析Autowire的值。我們進到這個方法中進行看一下:

在這個方法中會先判斷上一步取得的autowireValue是不是可以執行個體化為ObjectFactory 對象。上一步取得的autowireValue是RequestObjectFactory。我們看一下RequestObjectFactory這個類的内容:

很明顯的實作了ObjectFactory這個接口。接着RequestObjectFactory 是不能執行個體化為HttpServletRequest對象的。是以會進入到循環體中。接着進一步的判斷上一步取得的autowireValue的值能不能被序列化,以及requiredType是不是可以接口,從上面的分析可以看出這個條件也是成立的。是以最終生成的autowireValue的值是一個JDK動态代理生成的對象。InvocationHandler的實作類為:ObjectFactoryDelegatingInvocationHandler。是以我們在Controller層中所注入的HttpServletRequest其實是一個JDK動态代理生成的對象。這個是很關鍵的一個點!!!當我們在Controller中調用的時候:

會進入到ObjectFactoryDelegatingInvocationHandler的invoke方法中。我們去看一下:

這裡對equals方法、hashcode方法、toString方法進行了特殊處理。其他的方法則是直接執行method.invoke的方法。第一個參數為所調用的對象。是通過調用this.objectFactory.getObject()來擷取的。objectFactory,通過我們上面的分析我們知道它是RequestObjectFactory這個對象。是以我們去RequestObjectFactory這個類中看一下objectFactory這個方法:

在上面的代碼中調用了currentRequestAttributes().getRequest()。我們接着去currentRequestAttributes()這個方法中看一下:

看到這裡時,你是不是會有一種恍然大悟的感覺呢?((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();這種方式不也是我們經常在代碼中擷取HttpServletRequest對象的方式嗎?我在進一步的分析一下。看一下RequestContextHolder.currentRequestAttributes()這個方法:

通過getRequestAttributes()這個方法來擷取RequestAttributes對象。我們進入到getRequestAttributes()這個方法中繼續看一下:

發現是通過requestAttributesHolder或者inheritableRequestAttributesHolder的get()方法,來擷取的RequestAttributes對象。那麼requestAttributesHolder和inheritableRequestAttributesHolder是什麼呢?

兩個ThreadLocal的對象!!!到這裡總算是一切都真相大白了!!!每次都是從ThreadLocal對象中取的值,那還能不是線程安全的嗎?這裡在多說一句,什麼時候往這兩個ThreadLocal中放入值的呢?請接着往下看:org.springframework.web.servlet.FrameworkServlet#processRequest這個方法中有這樣的幾行代碼:

initContextHolders這個方法的代碼如下:

RequestContextHolder.setRequestAttributes這個方法的内容如下:

不需要再多說了。

這裡總結一下:Controller層中所注入的HttpServletReuqest的實作類為JDK動态代理生成的一個代理類,從Request中擷取值的時候是從ThreadLocal中得到的對象中的值。