在工做中遇到1個問題....咱們定義了一個Controller基類,全部Springmvc自定義的controller都繼承它....在它內部定義一個@Autowired HttpServletRequest request;可不能夠? 能不能從這個對象裏取requestParamters和attributes? 多線程之間會不會影響?web
初次思考,我想這應該是不行的.爲何呢?spring
注入bean是在spring容器啓動的時候...request的實現類是在tomcat裏(我使用的servlet容器是tomcat)....我又沒在spring的容器裏配置這個bean.注入應該是失敗的...tomcat
退一步說,就算是成功了....那注入的也就是1個對象而已.每次servlet接受到請求都會從新生成1個request...這明顯和以前啓動的那個對象不一樣吧....怎麼想都不可能成功...session
若是確實是這樣的....那就沒有這篇文章了....後來實踐了一下..發現這個注入是能夠的.使用起來取數據也沒任何問題....多線程
其實我那個時候debug看了一下,基本就知道爲何能夠取到數據了..可是我並不知道原理和Spring(Springmvc)的處理流程...因此如今研究了一下並記錄下來...mvc
首先給你們看一下在方法中注入request做爲參數和在成員域中注入request的 注入的request對象之間的區別....app
成員域注入的時候注入的是1個代理對象.是 AutowireUtils.ObjectFactoryDelegatingInvocationHandler的實例.less
方法注入的就是通常tomcat原生的requestFacade對象.async
因此這是不一樣的...ide
1 /** 2 * Reflective InvocationHandler for lazy access to the current target object. 3 */ 4 @SuppressWarnings("serial") 5 private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable { 6 7 private final ObjectFactory<?> objectFactory; 8 9 public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) { 10 this.objectFactory = objectFactory; 11 } 12 13 @Override 14 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 15 String methodName = method.getName(); 16 if (methodName.equals("equals")) { 17 // Only consider equal when proxies are identical. 18 return (proxy == args[0]); 19 } 20 else if (methodName.equals("hashCode")) { 21 // Use hashCode of proxy. 22 return System.identityHashCode(proxy); 23 } 24 else if (methodName.equals("toString")) { 25 return this.objectFactory.toString(); 26 } 27 try { 28 return method.invoke(this.objectFactory.getObject(), args); 29 } 30 catch (InvocationTargetException ex) { 31 throw ex.getTargetException(); 32 } 33 } 34 }
當代理對象(就是成員域request)的大部分方法被調用的時候,ObjectFactoryDelegatingInvocationHandler會使用objectFactory獲取對象(原生request),再調用對象上的方法.
而後咱們來看下XmlWebApplicationContext初始化到請求到進入controller裏幾個對注入request成員域有影響的步驟.
ApplicationContext的抽象實現類AbstractApplicationContext(基本是全部ac的父類)裏定義了ac的refresh方法(包含了使用BeanFactory注入bean)的流程..
1 @Override 2 public void refresh() throws BeansException, IllegalStateException { 3 synchronized (this.startupShutdownMonitor) { 4 // Prepare this context for refreshing. 5 // 記錄開始wac開始初始化的時間,設置激活標記,servlet的相關param設置到env(以前作過1次),校驗env中必須的props 6 prepareRefresh(); 7 8 // Tell the subclass to refresh the internal bean factory. 9 // 將舊的BF裏的bean刪掉,新建1個BF,設置部分屬性,加載XML配置文件 10 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 11 12 // Prepare the bean factory for use in this context. 13 // 1.設置BF解析bean配置須要用到的一些對象好比env. 2.註冊一些BeanPostProcessor好比ApplicationContextAwareProcessor去設置Aware須要的對象 14 // 3.忽略一些特定class注入的對象,設置一些特定class注入的對象爲指定值 15 // 4.將一些env中的properties map當作bean註冊到BF中 16 prepareBeanFactory(beanFactory); 17 18 try { 19 // Allows post-processing of the bean factory in context subclasses. 20 // 1.設置一個BeanPostProcess爲ServletContextAware的實現類注入servlet相關對象 21 // 2.在BF中增長requetsScope等Scope 22 // 3.把servletContext,Config,ServletInitParams,ServletAttribute當作Bean註冊到BF中 23 postProcessBeanFactory(beanFactory); 24 25 // Invoke factory processors registered as beans in the context. 26 // 初始化並調用BeanFactoryPostProcessor 27 invokeBeanFactoryPostProcessors(beanFactory); 28 29 // Register bean processors that intercept bean creation. 30 // 註冊BeanPostProcessors並註冊到BF中去 31 registerBeanPostProcessors(beanFactory); 32 33 // Initialize message source for this context. 34 initMessageSource(); 35 36 // Initialize event multicaster for this context. 37 initApplicationEventMulticaster(); 38 39 // Initialize other special beans in specific context subclasses. 40 onRefresh(); 41 42 // Check for listener beans and register them. 43 registerListeners(); 44 45 // Instantiate all remaining (non-lazy-init) singletons. 46 finishBeanFactoryInitialization(beanFactory); 47 48 // Last step: publish corresponding event. 49 finishRefresh(); 50 } catch (BeansException ex) { 51 logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); 52 53 // Destroy already created singletons to avoid dangling resources. 54 destroyBeans(); 55 56 // Reset 'active' flag. 57 cancelRefresh(ex); 58 59 // Propagate exception to caller. 60 throw ex; 61 } 62 } 63 }
其中有1個模板方法
postProcessBeanFactory(beanFactory);
這個方法容許AbstractApplicationContext的子類覆蓋它並實現對BF的定製(這個時候bean的defination路徑已經指定了,可是bean還沒加載).
AbstractRefreshableWebApplicationContext覆蓋了這個方法
1 /** 2 * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc. 3 * 1.設置一個BeanPostProcess爲ServletContextAware的實現類注入servlet相關對象 4 * 2.在BF中增長requetsScope等Scope 5 * 3.把servletContext,Config,ServletInitParams,ServletAttribute當作Bean註冊到BF中 6 * 7 */ 8 @Override 9 protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { 10 // 設置一個BeanPostProcess爲ServletContextAware的實現類注入servlet相關對象 11 beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig)); 12 beanFactory.ignoreDependencyInterface(ServletContextAware.class); 13 beanFactory.ignoreDependencyInterface(ServletConfigAware.class); 14 15 // 在BF中增長requetsScope等Scope 16 WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); 17 // 把servletContext,Config,ServletInitParams,ServletAttribute當作Bean註冊到BF中 18 WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig); 19 }
其中有一步
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
這裏設置了一些特殊的bean的scope,好比request,session,globalSession,application.(固然這個不是我這篇文章的主題.)
同時設置了一些特殊的autowired bean
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
ServletRequest的實現類(好比HttpServletRequest)被指定使用RequestObjectFactory注入.
RequestObjectFactory就是1個ObjectFactory就是前面ObjectFactoryDelegatingInvocationHandler裏的ObjectFactory.因此在成員域request對象上調用方法其實就是經過RequestObjectFactory獲取對象再調用方法.
1 /** 2 * Factory that exposes the current request object on demand. 3 */ 4 @SuppressWarnings("serial") 5 private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable { 6 7 @Override 8 public ServletRequest getObject() { 9 return currentRequestAttributes().getRequest(); 10 } 11 12 @Override 13 public String toString() { 14 return "Current HttpServletRequest"; 15 } 16 }
1 /** 2 * Return the current RequestAttributes instance as ServletRequestAttributes. 3 * 4 * @see RequestContextHolder#currentRequestAttributes() 5 */ 6 private static ServletRequestAttributes currentRequestAttributes() { 7 RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes(); 8 if (!(requestAttr instanceof ServletRequestAttributes)) { 9 throw new IllegalStateException("Current request is not a servlet request"); 10 } 11 return (ServletRequestAttributes) requestAttr; 12 }
RequestObjectFactory的getObject方法很簡單,就是調用靜態方法
RequestContextHolder.currentRequestAttributes().getRequest()
1 public static RequestAttributes currentRequestAttributes() throws IllegalStateException { 2 RequestAttributes attributes = getRequestAttributes(); 3 if (attributes == null) { 4 if (jsfPresent) { 5 attributes = FacesRequestAttributesFactory.getFacesRequestAttributes(); 6 } 7 if (attributes == null) { 8 throw new IllegalStateException("No thread-bound request found: " + 9 "Are you referring to request attributes outside of an actual web request, " + 10 "or processing a request outside of the originally receiving thread? " + 11 "If you are actually operating within a web request and still receive this message, " + 12 "your code is probably running outside of DispatcherServlet/DispatcherPortlet: " + 13 "In this case, use RequestContextListener or RequestContextFilter to expose the current request."); 14 } 15 } 16 return attributes; 17 }
1 /** 2 * Return the RequestAttributes currently bound to the thread. 3 * @return the RequestAttributes currently bound to the thread, 4 * or {@code null} if none bound 5 */ 6 public static RequestAttributes getRequestAttributes() { 7 RequestAttributes attributes = requestAttributesHolder.get(); 8 if (attributes == null) { 9 attributes = inheritableRequestAttributesHolder.get(); 10 } 11 return attributes; 12 }
1 private static final ThreadLocal<RequestAttributes> requestAttributesHolder = 2 new NamedThreadLocal<RequestAttributes>("Request attributes"); 3 4 private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = 5 new NamedInheritableThreadLocal<RequestAttributes>("Request context");
上面是一些關鍵方法
因此最終其實request是從threadlocal中取...
那麼request是何時設置到threadlocal中去的呢?
是在Springmvc的dispatcherServlet的父類FrameworkServlet裏操做的.
1 /** 2 * Delegate GET requests to processRequest/doService. 3 * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead}, 4 * with a {@code NoBodyResponse} that just captures the content length. 5 * @see #doService 6 * @see #doHead 7 */ 8 @Override 9 protected final void doGet(HttpServletRequest request, HttpServletResponse response) 10 throws ServletException, IOException { 11 12 processRequest(request, response); 13 } 14 15 /** 16 * Delegate POST requests to {@link #processRequest}. 17 * @see #doService 18 */ 19 @Override 20 protected final void doPost(HttpServletRequest request, HttpServletResponse response) 21 throws ServletException, IOException { 22 23 processRequest(request, response); 24 }
無論你是doGet仍是doPost仍是doXXX方法都是委託processRequest方法去作的.
1 /** 2 * Process this request, publishing an event regardless of the outcome. 3 * <p>The actual event handling is performed by the abstract 4 * {@link #doService} template method. 5 */ 6 protected final void processRequest(HttpServletRequest request, HttpServletResponse response) 7 throws ServletException, IOException { 8 9 long startTime = System.currentTimeMillis(); 10 Throwable failureCause = null; 11 12 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 13 LocaleContext localeContext = buildLocaleContext(request); 14 15 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 16 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); 17 18 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 19 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); 20 21 initContextHolders(request, localeContext, requestAttributes); 22 23 try { 24 doService(request, response); 25 } 26 catch (ServletException ex) { 27 failureCause = ex; 28 throw ex; 29 } 30 catch (IOException ex) { 31 failureCause = ex; 32 throw ex; 33 } 34 catch (Throwable ex) { 35 failureCause = ex; 36 throw new NestedServletException("Request processing failed", ex); 37 } 38 39 finally { 40 resetContextHolders(request, previousLocaleContext, previousAttributes); 41 if (requestAttributes != null) { 42 requestAttributes.requestCompleted(); 43 } 44 45 if (logger.isDebugEnabled()) { 46 if (failureCause != null) { 47 this.logger.debug("Could not complete request", failureCause); 48 } 49 else { 50 if (asyncManager.isConcurrentHandlingStarted()) { 51 logger.debug("Leaving response open for concurrent processing"); 52 } 53 else { 54 this.logger.debug("Successfully completed request"); 55 } 56 } 57 } 58 59 publishRequestHandledEvent(request, response, startTime, failureCause); 60 } 61 }
其中調用了
initContextHolders(request, localeContext, requestAttributes);
1 private void initContextHolders( 2 HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) { 3 4 if (localeContext != null) { 5 LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable); 6 } 7 if (requestAttributes != null) { 8 RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); 9 } 10 if (logger.isTraceEnabled()) { 11 logger.trace("Bound request context to thread: " + request); 12 } 13 }
就是在這裏設置到RequestContextHolder的threadlocal中去的...
1.在controller中注入的request是jdk動態代理對象,ObjectFactoryDelegatingInvocationHandler的實例.當咱們調用成員域request的方法的時候實際上是調用了objectFactory的getObject()對象的相關方法.這裏的objectFactory是RequestObjectFactory.
2.RequestObjectFactory的getObject實際上是從RequestContextHolder的threadlocal中去取值的.
3.請求剛進入springmvc的dispatcherServlet的時候會把request相關對象設置到RequestContextHolder的threadlocal中去.