1. 程式人生 > >spring之我見--Controller註冊到DispatchServlet請求處理(下)

spring之我見--Controller註冊到DispatchServlet請求處理(下)

1 DispatcherServlet請求分發

1.1 DispatcherServlet的初始化

在web.xml檔案裡,跟ContextLoaderListener形影不離的應該就是DispatcherServlet了,它一般做如下定義:

<servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class
>
<load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping>

DispatcherServlet是為SpringMVC服務的,在上一節中,我們知道了Controller如何被註冊,所有的Mapping資訊被儲存在MappingRegistry裡, DispatcherServlet接收到前臺發來的請求便會 根據 MappingRegistry做匹配,最後用反射做到請求任務分發。

我們先看看DispatcherServlet是怎麼初始化的,在DispatcherServlet的父類HttpServletBean中,每當IOC容器初始成功,會先呼叫init(),裡面有個重要的方法,
initServletBean(),會對DispatcherServlet做一些引數的初始化。

注意:

<load-on-startup>1</load-on-startup>

一般來說,servlet的load-on-startup不設定的話,第一次請求才會觸發init()方法,大於0的話就tomcat啟動的時候就會觸發。

一直我以為ApplicationContext在spring中只有一個,讓我驚訝的是,這次servlet初始化也建立了一個ApplicationContext,而且以IOC容器的ApplicationContext為父類。

/**
為這個servlet建立了一個WebApplicationContext 
     * Instantiate the WebApplicationContext for this servlet, either a default
     * {@link org.springframework.web.context.support.XmlWebApplicationContext}
     * or a {@link #setContextClass custom context class}, if set.
     * <p>This implementation expects custom contexts to implement the
     * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
     * interface. Can be overridden in subclasses.
     * <p>Do not forget to register this servlet instance as application listener on the
     * created context (for triggering its {@link #onRefresh callback}, and to call
     * {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
     * before returning the context instance.
     * @param parent the parent ApplicationContext to use, or {@code null} if none
     * @return the WebApplicationContext for this servlet
     * @see org.springframework.web.context.support.XmlWebApplicationContext
     */
    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name '" + getServletName() +
                    "': custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

        wac.setEnvironment(getEnvironment());
        //將初始化IOC容器時的ApplicationContext作為父容器
        wac.setParent(parent);
        wac.setConfigLocation(getContextConfigLocation());

        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }
protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
        //繼續進這個方法
            this.webApplicationContext = initWebApplicationContext();

            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }

然後跟進configureAndRefreshWebApplicationContext(),一個比較重要的就是添加了一個Listener,這個監聽會在ApplicationContext初始化完成後執行,Spring預留這個ApplicationListener就是給開發者提供方便做一些初始化操作。這個Listener做了什麼後面再說,最後還是跟所有ApplicationContext初始化一樣,走了refresh進行初始化。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            if (this.contextId != null) {
                wac.setId(this.contextId);
            }
            else {
                // Generate default id...
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
            }
        }

        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        //後面取dispatcher-servlet.xml檔案路徑有用
        wac.setNamespace(getNamespace());
        //新增一個listener,用於初始化
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

        // The wac environment's #initPropertySources will be called in any case when the context
        // is refreshed; do it eagerly here to ensure servlet property sources are in place for
        // use in any post-processing or initialization that occurs below prior to #refresh
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }

        postProcessWebApplicationContext(wac);
        applyInitializers(wac);
        wac.refresh();
    }

refresh方法裡比較重要的就是registerListeners() 和 finishRefresh() 裡的publishEvent(new ContextRefreshedEvent(this)),前者將剛剛增加的Listener加入ApplicationEventMulticaster例項,Spring提供了ApplicationEventMulticaster介面,負責管理ApplicationListener和釋出ApplicationEvent。後者從ApplicationEventMulticaster例項取出監聽,執行監聽的方法。下面是我們之前加入的那個監聽。

    /**
     * ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
     * only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance.
     */
    private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            FrameworkServlet.this.onApplicationEvent(event);
        }
    }

註冊監聽到ApplicationEventMulticaster例項

/**
     * Add beans that implement ApplicationListener as listeners.
     * Doesn't affect other listeners, which can be added without being beans.
     */
    protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

執行監聽

@Override
    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

這個監聽的主要進行Servlet初始化操作,其中initHandlerMappings(context) 就是將父容器的RequestMappingHandlerMapping提取出來,因為所有的Mapping資訊都存在裡面,然後轉成 handlerMappings供DispatchServlet分發請求。

/**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

具體的轉換過程

/**
     * 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.
                AnnotationAwareOrderComparator.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");
            }
        }
    }

至此,DispatchServlet的初始化告一段落,接下來就是具體的使用流程了。

1.2 DispatcherServlet的請求分發

還是老規矩看原始碼:

最先打交道的還是FrameworkServlet,是spring最基礎的servlet,繼承HttpServlet。

請求先從FrameworkServlet的service開始。

@Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
//因為父類不能處理PATCH請求,所以就直接processRequest處理
            processRequest(request, response);
        }
        else {
        //呼叫父類方法
            super.service(request, response);
        }
    }

請求鏈稍微有點兜兜轉轉, 然後根據 get,還是 post 等等又呼叫FrameworkServlet的do**方法,最後還是進入processRequest方法。
processRequest方法還沒折騰完,FrameworkServlet會呼叫DispatcherServlet的doService方法,這時才開始進入正題。

DispatcherServlet的任務就是分發請求。在經過doService()的一些前置工作後,會委託給doDispatch()

doDispatch大概流程就是 根據 request 拿到請求url, 然後從mappingRegistry 取出匹配的HandlerMethod, HandlerMethod通過DispatcherServlet的getHandler方法可以獲得一個HandlerExecutionChain物件例項,該例項封裝了一個handler處理物件和一些interceptors.

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;
                }

                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

HandlerMethod 轉成 HandlerExecutionChain 邏輯

@Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }

再往後還需要HandlerAdapter 的幫助,HandlerAdapter 根據HandlerExecutionChain 的對映資訊 最後通過反射呼叫controller方法,完成請求處理。

// 根據HandlerMethod取到HandlerAdapter 
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 真正呼叫Controller的地方
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

2 參考文獻

https://www.cnblogs.com/chenjunjie12321/articles/6118426.html
http://blog.csdn.net/wo541075754/article/details/71720984