原文鏈接:https://www.cnblogs.com/abcwt112/p/7777258.html
原文作者:abcwt112
主題
在工作中遇到1個問題....我們定義了一個Controller基類,所有Springmvc自定義的controller都繼承它....在它內部定義一個@Autowired HttpServletRequest request;可不可以? 能不能從這個對象里取requestParamters和attributes? 多線程之間會不會影響?
思考
初次思考,我想這應該是不行的.為什么呢?
注入bean是在spring容器啟動的時候...request的實現類是在tomcat里(我使用的servlet容器是tomcat)....我又沒在spring的容器里配置這個bean.注入應該是失敗的...
退一步說,就算是成功了....那注入的也就是1個對象而已.每次servlet接受到請求都會重新生成1個request...這明顯和之前啟動的那個對象不同吧....怎么想都不可能成功...
如果確實是這樣的....那就沒有這篇文章了....后來實踐了一下..發現這個注入是可以的.使用起來取數據也沒任何問題....
其實我那個時候debug看了一下,基本就知道為什么可以取到數據了..但是我並不知道原理和Spring(Springmvc)的處理流程...所以現在研究了一下並記錄下來...
原理
首先給大家看一下在方法中注入request作為參數和在成員域中注入request的 注入的request對象之間的區別....
成員域注入的時候注入的是1個代理對象.是 AutowireUtils.ObjectFactoryDelegatingInvocationHandler的實例.
方法注入的就是一般tomcat原生的requestFacade對象.
所以這是不同的...
/** * Reflective InvocationHandler for lazy access to the current target object. */ @SuppressWarnings("serial") private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable { private final ObjectFactory<?> objectFactory; public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) { this.objectFactory = objectFactory; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0]); } else if (methodName.equals("hashCode")) { // Use hashCode of proxy. return System.identityHashCode(proxy); } else if (methodName.equals("toString")) { return this.objectFactory.toString(); } try { return method.invoke(this.objectFactory.getObject(), args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } }
當代理對象(就是成員域request)的大部分方法被調用的時候,ObjectFactoryDelegatingInvocationHandler會使用objectFactory獲取對象(原生request),再調用對象上的方法.
然后我們來看下XmlWebApplicationContext初始化到請求到進入controller里幾個對注入request成員域有影響的步驟.
refresh方法和postProcessBeanFactory方法
ApplicationContext的抽象實現類AbstractApplicationContext(基本是所有ac的父類)里定義了ac的refresh方法(包含了使用BeanFactory注入bean)的流程..
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 記錄開始wac開始初始化的時間,設置激活標記,servlet的相關param設置到env(之前做過1次),校驗env中必須的props prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 將舊的BF里的bean刪掉,新建1個BF,設置部分屬性,加載XML配置文件 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 1.設置BF解析bean配置需要用到的一些對象比如env. 2.注冊一些BeanPostProcessor比如ApplicationContextAwareProcessor去設置Aware需要的對象 // 3.忽略一些特定class注入的對象,設置一些特定class注入的對象為指定值 // 4.將一些env中的properties map當做bean注冊到BF中 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 1.設置一個BeanPostProcess為ServletContextAware的實現類注入servlet相關對象 // 2.在BF中增加requetsScope等Scope // 3.把servletContext,Config,ServletInitParams,ServletAttribute當做Bean注冊到BF中 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 初始化並調用BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注冊BeanPostProcessors並注冊到BF中去 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
其中有1個模板方法
postProcessBeanFactory(beanFactory);
這個方法允許AbstractApplicationContext的子類覆蓋它並實現對BF的定制(這個時候bean的defination路徑已經指定了,但是bean還沒加載).
AbstractRefreshableWebApplicationContext覆蓋了這個方法
/** * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc. * 1.設置一個BeanPostProcess為ServletContextAware的實現類注入servlet相關對象 * 2.在BF中增加requetsScope等Scope * 3.把servletContext,Config,ServletInitParams,ServletAttribute當做Bean注冊到BF中 * */ @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 設置一個BeanPostProcess為ServletContextAware的實現類注入servlet相關對象 beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig)); beanFactory.ignoreDependencyInterface(ServletContextAware.class); beanFactory.ignoreDependencyInterface(ServletConfigAware.class); // 在BF中增加requetsScope等Scope WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); // 把servletContext,Config,ServletInitParams,ServletAttribute當做Bean注冊到BF中 WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig); }
其中有一步
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
RequestObjectFactory就是1個ObjectFactory就是前面ObjectFactoryDelegatingInvocationHandler里的ObjectFactory.所以在成員域request對象上調用方法其實就是通過RequestObjectFactory獲取對象再調用方法.
/** * Factory that exposes the current request object on demand. */ @SuppressWarnings("serial") private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable { @Override public ServletRequest getObject() { return currentRequestAttributes().getRequest(); } @Override public String toString() { return "Current HttpServletRequest"; } }
/** * Return the current RequestAttributes instance as ServletRequestAttributes. * @see RequestContextHolder#currentRequestAttributes() */ private static ServletRequestAttributes currentRequestAttributes() { RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes(); if (!(requestAttr instanceof ServletRequestAttributes)) { throw new IllegalStateException("Current request is not a servlet request"); } return (ServletRequestAttributes) requestAttr; }
RequestObjectFactory的getObject方法很簡單,就是調用靜態方法
RequestContextHolder.currentRequestAttributes().getRequest()
RequestContextHolder
public static RequestAttributes currentRequestAttributes() throws IllegalStateException { RequestAttributes attributes = getRequestAttributes(); if (attributes == null) { if (jsfPresent) { attributes = FacesRequestAttributesFactory.getFacesRequestAttributes(); } if (attributes == null) { throw new IllegalStateException("No thread-bound request found: " + "Are you referring to request attributes outside of an actual web request, " + "or processing a request outside of the originally receiving thread? " + "If you are actually operating within a web request and still receive this message, " + "your code is probably running outside of DispatcherServlet/DispatcherPortlet: " + "In this case, use RequestContextListener or RequestContextFilter to expose the current request."); } } return attributes; }
/** * Return the RequestAttributes currently bound to the thread. * @return the RequestAttributes currently bound to the thread, * or {@code null} if none bound */ public static RequestAttributes getRequestAttributes() { RequestAttributes attributes = requestAttributesHolder.get(); if (attributes == null) { attributes = inheritableRequestAttributesHolder.get(); } return attributes; }
private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<RequestAttributes>("Request attributes"); private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<RequestAttributes>("Request context");
上面是一些關鍵方法
所以最終其實request是從threadlocal中取...
FrameworkServlet
那么request是什么時候設置到threadlocal中去的呢?
是在Springmvc的dispatcherServlet的父類FrameworkServlet里操作的.
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
不管你是doGet還是doPost還是doXXX方法都是委托processRequest方法去做的.
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } }
其中調用了
initContextHolders(request, localeContext, requestAttributes);
private void initContextHolders( HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) { if (localeContext != null) { LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable); } if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } }
就是在這里設置到RequestContextHolder的threadlocal中去的...
小結
1.在controller中注入的request是jdk動態代理對象,ObjectFactoryDelegatingInvocationHandler的實例.當我們調用成員域request的方法的時候其實是調用了objectFactory的getObject()對象的相關方法.這里的objectFactory是RequestObjectFactory.
2.RequestObjectFactory的getObject其實是從RequestContextHolder的threadlocal中去取值的.
3.請求剛進入springmvc的dispatcherServlet的時候會把request相關對象設置到RequestContextHolder的threadlocal中去.
對於原文的補充
原文沒有詳細描述成員域request注入的具體過程,補充如下:
ApplicationContext的初始化調用過程如下:
在refresh方法被調用后,會調用org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates
protected Map<String, Object> findAutowireCandidates( String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length); for (Class<?> autowiringType : this.resolvableDependencies.keySet()) { if (autowiringType.isAssignableFrom(requiredType)) { Object autowiringValue = this.resolvableDependencies.get(autowiringType); autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); if (requiredType.isInstance(autowiringValue)) { result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) { result.put(candidateName, getBean(candidateName)); } } if (result.isEmpty()) { DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); for (String candidateName : candidateNames) { if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { result.put(candidateName, getBean(candidateName)); } } } return result; }
該方法內部會調用:
org.springframework.beans.factory.support.AutowireUtils#resolveAutowiringValue
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) { if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) { ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue; if (autowiringValue instanceof Serializable && requiredType.isInterface()) { autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(), new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory)); } else { return factory.getObject(); } } return autowiringValue; }
可以看到,如果需要注入的對象是接口類型的話,則為其創建代理對象。