spring mvc DispatcherServlet詳解之一---處理請求深刻解析

要深刻理解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整個過程的細節一章以內很難描述的面面俱到,只能分析部分流程,想了解更具體的實現須要從源代碼中去尋找。

相關文章
相關標籤/搜索