要深刻理解spring mvc的工做流程,就須要先了解spring mvc的架構:前端
從上圖能夠看到 前端控制器DispatcherServlet在其中起着主導做用,理解了DispatcherServlet 就徹底能夠說弄清楚了spring mvc。java
爲了加深對spring mvc的整個工做流程的理解,本文從分析DispatcherServlet的工做過程來一窺spring mvc的整個面貌。web
1. 初始化spring
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //文件上傳解析,若是請求類型是multipart將經過MultipartResolver進行文件上傳解析;
initLocaleResolver(context); //本地化解析
initThemeResolver(context); //主題解析
initHandlerMappings(context); //經過HandlerMapping,將請求映射處處理器
initHandlerAdapters(context); //經過HandlerAdapter支持多種類型的處理器
initHandlerExceptionResolvers(context); //若是執行過程當中遇到異常將交給HandlerExceptionResolver來解析
initRequestToViewNameTranslator(context); //直接解析請求到視圖名
initViewResolvers(context); //經過ViewResolver解析邏輯視圖名到具體視圖實現
initFlashMapManager(context); //flash映射管理器
}架構
單個resolver
initMultipartResolver,initLocaleResolver,initThemeResolver,initRequestToViewNameTranslator,initFlashMapManager 這五個初始化方法流程相同,都是使用
context.getBean(String name, Class<FlashMapManager> requiredType)的方式獲取到相應的Resolver。以initMultipartResolver爲例,見以下:
/** * Initialize the MultipartResolver used by this class. * <p>If no bean is defined with the given name in the BeanFactory for this namespace, * no multipart handling is provided. */ private void initMultipartResolver(ApplicationContext context) { try { this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using MultipartResolver [" + this.multipartResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isDebugEnabled()) { logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + "': no multipart request handling provided"); } } }
多個resolvermvc
initHandlerMappings,initHandlerAdapters,initHandlerExceptionResolvers,initViewResolvers 獲取方式相同,使用:
BeanFactoryUtils.beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<HandlerMapping> type, boolean includeNonSingletons, boolean allowEagerInit)
的方式獲取到相應的Resolver。以initHandlerMappings爲例,見以下:
/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. OrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
那麼深刻看一下BeanFactoryUtils.beansOfTypeIncludingAncestors 究竟作了什麼?返回指定類型和子類型的全部bean,若該bean factory 是一個繼承類型的beanFactory,這個方法也會獲取祖宗factory中定義的指定類型的bean。app
/** * Return all beans of the given type or subtypes, also picking up beans defined in * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory. * The returned Map will only contain beans of this type. * <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set, * which means that FactoryBeans will get initialized. If the object created by the * FactoryBean doesn't match, the raw FactoryBean itself will be matched against the * type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked * (which doesn't require initialization of each FactoryBean). * <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level, * i.e. such beans will be returned from the lowest factory that they are being found in, * hiding corresponding beans in ancestor factories.</b> This feature allows for * 'replacing' beans by explicitly choosing the same bean name in a child factory; * the bean in the ancestor factory won't be visible then, not even for by-type lookups. * @param lbf the bean factory * @param type type of bean to match * @param includeNonSingletons whether to include prototype or scoped beans too * or just singletons (also applies to FactoryBeans) * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and * <i>objects created by FactoryBeans</i> (or by factory methods with a * "factory-bean" reference) for the type check. Note that FactoryBeans need to be * eagerly initialized to determine their type: So be aware that passing in "true" * for this flag will initialize FactoryBeans and "factory-bean" references. * @return the Map of matching bean instances, or an empty Map if none * @throws BeansException if a bean could not be created */ public static <T> Map<String, T> beansOfTypeIncludingAncestors( ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException { Assert.notNull(lbf, "ListableBeanFactory must not be null"); Map<String, T> result = new LinkedHashMap<String, T>(4); result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit)); if (lbf instanceof HierarchicalBeanFactory) { HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf; if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) { Map<String, T> parentResult = beansOfTypeIncludingAncestors( (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit); for (Map.Entry<String, T> entry : parentResult.entrySet()) { String beanName = entry.getKey(); if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) { result.put(beanName, entry.getValue()); } } } } return result; }
2. 提供服務框架
咱們來看看這個servlet是如何提供服務的?eclipse
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { doDispatch(request, response); } finally { if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return; } // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
從上面咱們能夠看到,提供服務只要分4步:async
1. 保存現場。保存request 熟悉的快照,以便能在必要時恢復。
2. 將框架須要的對象放入request中,以便view和handler使用。
3. 請求分發服務.
4. 恢復現場。
其中最重要的是請求分發服務:
/** * Process the actual dispatching to the handler. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }
分發過程以下:
1. 判斷是否設置了multipart resolver,設置的話轉換爲multipart request,沒有的話則繼續下面的步驟。
2. 根據當前request,獲取hangdler。
3. 根據當前request,獲取HandlerAdapter。
4. 若是支持http請求頭,處理 last-modified header請求頭。
5. 應用已註冊interceptor的preHandle方法
6. HandlerAdapter處理請求。
7. 設置默認視圖。
8. 應用已註冊interceptor的postHandle方法。
9. 處理異常或者視圖渲染。
小結:
DispatherServlet整個過程的細節一章以內很難描述的面面俱到,只能分析部分流程,想了解更具體的實現須要從源代碼中去尋找。