我們可以在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 }
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 }
附:
1. Spring能實現在多線程環境下,將各個線程的request進行隔離,且准確無誤的進行注入,奧秘就是ThreadLocal
2. Spring中還可以直接注入ApplicationContext