在Spring的bean中注入HttpServletRequest解密


我們可以在Spring的bean中輕松的注入HttpServletRequest,使用@Autowired HttpServletRequest request;就可以了。

但是,為什么我們可以直接這樣用呢?

原因肯定是Spring在容器初始化的時候就將HttpServletRequest注冊到了容器中。

那么我們就查原碼,發現在WebApplicationContextUtils.registerWebApplicationScopes(ConfigurableListableBeanFactory, ServletContext)中實現了這個功能。

這個方法是在AbstractApplicationContext.refresh()中進行調用的,也就是在Spring容器初始化的時候,將web相關的對象注冊到了容器中。

具體可以看下面的原碼圖片:

 

 

 

 

調用處:

 

問題: 

注入 HttpServletRequest 為什么每次都能拿到最新的?bean 的依耐關系不是容器啟動后就確定了嗎?

解析:
spring 注冊的 HttpServletRequest 類型的 bean 是一個 RequestObjectFactory,如果進行依賴注入時是通過 RequestObjectFactory.getObject() 獲取 request 對象的話,那么依賴關系在這個時候就確定了,這樣肯定不能達到每次 http 請求都拿到一個新的 HttpServletRequest 對象的目的。
寫一個 controller ,注入 HttpServletRequest,打斷點查看注入的對象是一個 JDK 代理對象, 對應的 InvocationHandler 是 AutowireUtils$ObjectFactoryDelegatingInvocationHandler。通過查找 ObjectFactoryDelegatingInvocationHandler 使用的地方,發現是在 AutowireUtils.resolveAutowiringValue() 時創建的代理對象。再在這個方法上打個斷點,重新啟動容器,就可以發現,HttpServletRequest 是在 XxController 這個 bean 注入依賴屬性時 (populateBean() )調用的 AutowireUtils.resolveAutowiringValue() 創建的代理。
這樣就說的通了:因為 spring 在注入 HttpServletRequest 時,發現如果注入的是 一個 ObjectFactory 類型的對象時,就會將注入的 bean 替換成一個 JDK 動態代理對象,代理對象在執行 HttpServletRequest 對象里的方法時,就會通過 RequestObjectFactory.getObject() 獲取一個 新的 request 對象來執行。
 

RequestObjectFactory

 1 private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
 2     @Override
 3     public ServletRequest getObject() {
 4         return currentRequestAttributes().getRequest();
 5     }
 6 
 7     @Override
 8     public String toString() {
 9         return "Current HttpServletRequest";
10     }
11 }
View Code

ObjectFactoryDelegatingInvocationHandler

 1 private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
 2 
 3     private final ObjectFactory<?> objectFactory;
 4 
 5     public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
 6         this.objectFactory = objectFactory;
 7     }
 8 
 9     @Override
10     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
11         String methodName = method.getName();
12         if (methodName.equals("equals")) {
13             // Only consider equal when proxies are identical.
14             return (proxy == args[0]);
15         }
16         else if (methodName.equals("hashCode")) {
17             // Use hashCode of proxy.
18             return System.identityHashCode(proxy);
19         }
20         else if (methodName.equals("toString")) {
21             return this.objectFactory.toString();
22         }
23         try {
24             return method.invoke(this.objectFactory.getObject(), args);
25         }
26         catch (InvocationTargetException ex) {
27             throw ex.getTargetException();
28         }
29     }
30 }
View Code

 

 

附:

1. Spring能實現在多線程環境下,將各個線程的request進行隔離,且准確無誤的進行注入,奧秘就是ThreadLocal

 

2. Spring中還可以直接注入ApplicationContext


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM